diff --git a/Content/Editor/Camera/M_Camera.flax b/Content/Editor/Camera/M_Camera.flax index 7dc7ae92d..96a7cfe6f 100644 --- a/Content/Editor/Camera/M_Camera.flax +++ b/Content/Editor/Camera/M_Camera.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3719d6696fc99d392e692c5a33c3513194ea9b0dcbe4b912d1fd03ff6806de8 -size 29740 +oid sha256:f74c9fbe0a662098539cce65d1f3d0d4804344a49d78682f073596deb6cbcfbe +size 30376 diff --git a/Content/Editor/CubeTexturePreviewMaterial.flax b/Content/Editor/CubeTexturePreviewMaterial.flax index c16e7b2c2..0e20158a6 100644 --- a/Content/Editor/CubeTexturePreviewMaterial.flax +++ b/Content/Editor/CubeTexturePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:251f631a68805a22eb485c75e23d292ca340e1c231e0b9cc2d2beb1996c39334 -size 31293 +oid sha256:ceae144560a19ab5d71296f75918719619f4d4069a96b2bc46e54dc0cb71c7db +size 31929 diff --git a/Content/Editor/DebugMaterials/DDGIDebugProbes.flax b/Content/Editor/DebugMaterials/DDGIDebugProbes.flax index fb9e3c5cb..60c62fba3 100644 --- a/Content/Editor/DebugMaterials/DDGIDebugProbes.flax +++ b/Content/Editor/DebugMaterials/DDGIDebugProbes.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b9ba31cda46e670ae646719a813cab38dd1909003bada0bd391ed0b5a1c31f35 -size 39248 +oid sha256:a57f6ae32a6b5b67540914583757752fee027ce187b2ff5655e1de774cdac3fb +size 41699 diff --git a/Content/Editor/DebugMaterials/SingleColor/Particle.flax b/Content/Editor/DebugMaterials/SingleColor/Particle.flax index 2e64844ee..afd40c5ef 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Particle.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Particle.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:90a2465be9b88b4bdd1964c33583fce00583d393e9e13135fd44533072e6efe3 -size 31900 +oid sha256:4e43c8be4f61fcb229f36663d2fe96085369d61a6cee62715bb7396e8db666b2 +size 32611 diff --git a/Content/Editor/DebugMaterials/SingleColor/Surface.flax b/Content/Editor/DebugMaterials/SingleColor/Surface.flax index 857f328a7..57be70ca1 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Surface.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Surface.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8e018c5451c59c0d51005afb368267f9a37f7af02f20e2da4043298ab694c02 -size 29636 +oid sha256:5bbb29da2c223cf952b3de6c13a3001f6b0594077a19ba484f0141472fcd5929 +size 30272 diff --git a/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax b/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax index cc0df4ad4..4918d4cb0 100644 --- a/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax +++ b/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f17088e79d90261b8f630807173c4544a35558956ad4cb6ef0854f8749b5504 -size 31432 +oid sha256:50682179fbdcbfaffe882cb20c988538985a658b8080742c95316e029eafc54e +size 32068 diff --git a/Content/Editor/DefaultFontMaterial.flax b/Content/Editor/DefaultFontMaterial.flax index 7305b03a3..b63adfc39 100644 --- a/Content/Editor/DefaultFontMaterial.flax +++ b/Content/Editor/DefaultFontMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:583f8fc1103a204c64742c370e65fe9b012f03379c36b0f8fb1ed56a89c7a9ba -size 29815 +oid sha256:c018f5ec479f05333b997abad26f1bb9f633dacef883eecf69a907413f45a971 +size 30451 diff --git a/Content/Editor/Gizmo/FoliageBrushMaterial.flax b/Content/Editor/Gizmo/FoliageBrushMaterial.flax index 7be601ae2..7184acd1b 100644 --- a/Content/Editor/Gizmo/FoliageBrushMaterial.flax +++ b/Content/Editor/Gizmo/FoliageBrushMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eba32bfcb4daf4103f78ef0da45af03a39b7524609b77fc0abcf77da0a46b052 -size 35450 +oid sha256:bc20821469f9f6daad085ed8015b4ae2c096923b885ecfc45a5aa3a379957e6b +size 36086 diff --git a/Content/Editor/Gizmo/Material.flax b/Content/Editor/Gizmo/Material.flax index de6af6bf6..5375afa49 100644 --- a/Content/Editor/Gizmo/Material.flax +++ b/Content/Editor/Gizmo/Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5a38dff84bb7567a36681da8f49cdb42c2983716c9ce22066041e01981b6f38f -size 31970 +oid sha256:c7922ca3c520f573b61e4381971c4780395b897f5e170190b7c958c34bc82cc6 +size 32606 diff --git a/Content/Editor/Gizmo/MaterialWire.flax b/Content/Editor/Gizmo/MaterialWire.flax index 697c75f67..68df95b84 100644 --- a/Content/Editor/Gizmo/MaterialWire.flax +++ b/Content/Editor/Gizmo/MaterialWire.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:089c20c734028de1d6e377c1128d37af340772daee835920e0b7f2a47277306a -size 31183 +oid sha256:e7c775989c659d19d415bd739adea83f3d2e0f4ba31ffee1bbfb4a952b611a77 +size 31819 diff --git a/Content/Editor/Gizmo/SelectionOutlineMaterial.flax b/Content/Editor/Gizmo/SelectionOutlineMaterial.flax index 343b9d359..29dbc3ace 100644 --- a/Content/Editor/Gizmo/SelectionOutlineMaterial.flax +++ b/Content/Editor/Gizmo/SelectionOutlineMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e659db97079c9e4c12ed202f8f3121ed54bc72d666ee8e26012525bf6c47952 -size 15979 +oid sha256:abeacff17a677e6559fdddc464027eae7773f48384aecfb706c2c33aac1df18d +size 15899 diff --git a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax index 609b91ac6..7bf1d0f0c 100644 --- a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax +++ b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08e51480d1e5a58d89537fecc9897a32c1f641081e0ca0e53991dc431fbafd18 -size 30749 +oid sha256:903de465290b59884856eb169496b5aa76018b46af84c19a49ae491eb174eac7 +size 31385 diff --git a/Content/Editor/Highlight Material.flax b/Content/Editor/Highlight Material.flax index 21da6298d..f781fc603 100644 --- a/Content/Editor/Highlight Material.flax +++ b/Content/Editor/Highlight Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78c1b74d0c7ca3729e918ebd071b141f816792a539c1fe0a637163bba9f7de45 -size 29782 +oid sha256:deaccd404be601e04bd6e9e06c46d69016067b3e0bb310a13f0ebeda2da0a138 +size 30418 diff --git a/Content/Editor/Icons/IconsMaterial.flax b/Content/Editor/Icons/IconsMaterial.flax index fd49b5205..4f247ff1d 100644 --- a/Content/Editor/Icons/IconsMaterial.flax +++ b/Content/Editor/Icons/IconsMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b4ebf0e6605a5afb03cd3efbf1a35a8ee2d921d343a59c68c4e207ea1d37c974 -size 29710 +oid sha256:63eefd35f042a8266a7096d4b91dd22a9f066e443c862782c741f166ef7c95e2 +size 30346 diff --git a/Content/Editor/IesProfilePreviewMaterial.flax b/Content/Editor/IesProfilePreviewMaterial.flax index 3c2a05761..3e4f62c7c 100644 --- a/Content/Editor/IesProfilePreviewMaterial.flax +++ b/Content/Editor/IesProfilePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f011ee46428e752f6c719ab548971ae527c6536e1bf1e5de5b7cda7f901bea6 -size 18254 +oid sha256:da8bf8669e1cc74900dc4970702dfa3e096bf290ca4098080ba75e303a2f93db +size 18477 diff --git a/Content/Editor/MaterialTemplates/Surface.shader b/Content/Editor/MaterialTemplates/Surface.shader index ff88f1b40..ebf020597 100644 --- a/Content/Editor/MaterialTemplates/Surface.shader +++ b/Content/Editor/MaterialTemplates/Surface.shader @@ -617,14 +617,14 @@ void PS_Depth(PixelInput input) #if _PS_QuadOverdraw -#include "./Flax/Editor/QuadOverdraw.hlsl" +//#include "./Flax/Editor/QuadOverdraw.hlsl" // Pixel Shader function for Quad Overdraw Pass (editor-only) [earlydepthstencil] META_PS(USE_EDITOR, FEATURE_LEVEL_SM5) void PS_QuadOverdraw(float4 svPos : SV_Position, uint primId : SV_PrimitiveID) { - DoQuadOverdraw(svPos, primId); + //DoQuadOverdraw(svPos, primId); } #endif diff --git a/Content/Editor/Particles/Particle Material Color.flax b/Content/Editor/Particles/Particle Material Color.flax index afb352921..b90de33c7 100644 --- a/Content/Editor/Particles/Particle Material Color.flax +++ b/Content/Editor/Particles/Particle Material Color.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:240cf4a974876cf7d912fa13ccce11f8e2127690709cc8375dcb39019acf3d1d -size 30092 +oid sha256:4322707c45ac9a14d0bd1096b232d435696e8a362e67f89727875b72a16e9e09 +size 30803 diff --git a/Content/Editor/Particles/Smoke Material.flax b/Content/Editor/Particles/Smoke Material.flax index 66dad0416..248da9ba5 100644 --- a/Content/Editor/Particles/Smoke Material.flax +++ b/Content/Editor/Particles/Smoke Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:404950e170fecb6bdd772ca06724f11e2ae3afc737c3ae6f6d72f3cae25615ce -size 36203 +oid sha256:2aea33c284ec7e73d68fe767bcef5ff8b89590e3c96d93e6265ecfd18a23832c +size 36914 diff --git a/Content/Editor/Particles/Smoke.flax b/Content/Editor/Particles/Smoke.flax index e68821034..3450f64e4 100644 --- a/Content/Editor/Particles/Smoke.flax +++ b/Content/Editor/Particles/Smoke.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:84c4ea742b6b4775bc518731ecb56d7fe202302b3bdb61f1dcdf8727ade94353 -size 16019 +oid sha256:eacbc2471d622eb8a483036f01bbbbdde25cd3ab5f9e7b3ca1ec52634e9795f6 +size 16157 diff --git a/Content/Editor/Particles/Sparks.flax b/Content/Editor/Particles/Sparks.flax index 8b076afdc..73b73b304 100644 --- a/Content/Editor/Particles/Sparks.flax +++ b/Content/Editor/Particles/Sparks.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a6dc75b65f0eb1a66b12fbe4e20091b55aed9143a8574e7a9dfc000413eb2f6 -size 15081 +oid sha256:fab2f2ed2faec08c4c834e10dc730064baa2d92142f3bd06c243a44d36dbec63 +size 15275 diff --git a/Content/Editor/SpriteMaterial.flax b/Content/Editor/SpriteMaterial.flax index 639e047a3..518dac15e 100644 --- a/Content/Editor/SpriteMaterial.flax +++ b/Content/Editor/SpriteMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:903d85af5a3f78da2ced6896d274c954fa01361f3c3b612a23834c5a6f00d3b3 -size 30828 +oid sha256:7ae78cb3278f3363b23ec5a2fbae387afbd7ecbcce1829966cfffefac5449300 +size 31464 diff --git a/Content/Editor/TexturePreviewMaterial.flax b/Content/Editor/TexturePreviewMaterial.flax index cf603fc6a..9d86a708e 100644 --- a/Content/Editor/TexturePreviewMaterial.flax +++ b/Content/Editor/TexturePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50c530e40174d835aa0daa1d2a0a6cc06975c173e6aa2672238891c580a4ad52 -size 10413 +oid sha256:ec2f3b0fc4bc04f965159675e45e82a7ff0c6f2ac3625636b0f0b745ef19fde4 +size 10673 diff --git a/Content/Editor/Wires Debug Material.flax b/Content/Editor/Wires Debug Material.flax index fb4722aa9..c9a30f2f0 100644 --- a/Content/Editor/Wires Debug Material.flax +++ b/Content/Editor/Wires Debug Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c99e922203b5bad2e85747216ba53790bcd8ae97fe97a575aeacfe327001a815 -size 29782 +oid sha256:eb5552c4a20c9c11a5a6733c8dfdea8f8931eccd8f4014f847884c2983b951ae +size 30418 diff --git a/Content/Engine/DefaultDeformableMaterial.flax b/Content/Engine/DefaultDeformableMaterial.flax index 7dad19bdc..48018f511 100644 --- a/Content/Engine/DefaultDeformableMaterial.flax +++ b/Content/Engine/DefaultDeformableMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:16fdcea1d354ccd230bbc25ced1763e53e51c9cd24876d2a9dcf2e448fc7d800 -size 19140 +oid sha256:71d41fd76f1366700d32d9f7c5d3f18fa75d6ba5e859f723c36e41ac2b46f69e +size 19131 diff --git a/Content/Engine/DefaultMaterial.flax b/Content/Engine/DefaultMaterial.flax index dcf11c4d9..3c2476654 100644 --- a/Content/Engine/DefaultMaterial.flax +++ b/Content/Engine/DefaultMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:54f3db43dd7c475d8f2ac439e4df0d119ebfe06f32430bb52515383b7defbc1b -size 31528 +oid sha256:476755a18369bc6a56792f56f3dfcf1b7c9c1e4b999881af86423ad778875b74 +size 32155 diff --git a/Content/Engine/DefaultTerrainMaterial.flax b/Content/Engine/DefaultTerrainMaterial.flax index c4d0b0039..f7720e532 100644 --- a/Content/Engine/DefaultTerrainMaterial.flax +++ b/Content/Engine/DefaultTerrainMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:702f071488e43af1c317f7b7b0d192b8e97f45379c24d90e8ec6f7def7294891 -size 23633 +oid sha256:7a2d8f9c7c4739de1de26b996e590bd8600a626e950770c948ec93fde0788c6f +size 23624 diff --git a/Content/Engine/SingleColorMaterial.flax b/Content/Engine/SingleColorMaterial.flax index 8dcda00ba..ee147cea2 100644 --- a/Content/Engine/SingleColorMaterial.flax +++ b/Content/Engine/SingleColorMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2cb8ff6c5c7376790725d9b9ffb698244f7977a4aa490546c2ee16e5cf6c1cfb -size 29837 +oid sha256:f1ef17a46abb31f26374c8750bd01105cb43b6bfb9cd95089ae8113856a26149 +size 30473 diff --git a/Content/Engine/SkyboxMaterial.flax b/Content/Engine/SkyboxMaterial.flax index c191813df..d7c495f6a 100644 --- a/Content/Engine/SkyboxMaterial.flax +++ b/Content/Engine/SkyboxMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e030a81391a5b7e6d9e57d2fdb9a7402f76979df03b6e1207dbba7b316f80c7 -size 31035 +oid sha256:4e7cdcdbe88a895d76a6076f5309c4795c5119d2cca0a3c5d603cdc2834b963e +size 31671 diff --git a/Content/Shaders/GI/DDGI.flax b/Content/Shaders/GI/DDGI.flax index 172af5014..5fea80f23 100644 --- a/Content/Shaders/GI/DDGI.flax +++ b/Content/Shaders/GI/DDGI.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:639f5bc194e1108f6c43523bce2483d25381a0ee9e26b53f213a1edefd4adcae -size 19178 +oid sha256:220cdb9a73d181fb2dccf59de1169e57d42537b8aaf9deba495dc753fd19c8b6 +size 22031 diff --git a/Content/Shaders/GI/GlobalSurfaceAtlas.flax b/Content/Shaders/GI/GlobalSurfaceAtlas.flax index ad3fe2b33..dedc2d667 100644 --- a/Content/Shaders/GI/GlobalSurfaceAtlas.flax +++ b/Content/Shaders/GI/GlobalSurfaceAtlas.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:85611a5c4fefa268334bb564654c3631ac676354f2a61b388ca9fa09671d4686 -size 11551 +oid sha256:cd43051dd43bb23828e1f2f2001d9a2b008ebad085df44f1188a0e482f6f7e34 +size 12851 diff --git a/Content/Shaders/GlobalSignDistanceField.flax b/Content/Shaders/GlobalSignDistanceField.flax index 45eb8f6a1..26dcc8847 100644 --- a/Content/Shaders/GlobalSignDistanceField.flax +++ b/Content/Shaders/GlobalSignDistanceField.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dc0f1e9b52d89f6bfa8c590deeffb464988d7d666c612592968401da52ad8d2a -size 10668 +oid sha256:a3167ce312acd3367b1107eb442be0177ea2c788800fa6c13b11d0edbae8b559 +size 10992 diff --git a/Content/Shaders/VolumetricFog.flax b/Content/Shaders/VolumetricFog.flax index 5dbdb545b..cb7fe197b 100644 --- a/Content/Shaders/VolumetricFog.flax +++ b/Content/Shaders/VolumetricFog.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4908ae3d09694599b3eb3e3fcab07e1b948e68a3dcfbe663a2c3579f566328c -size 13823 +oid sha256:820a1dcf3ed2631677201e633aa759615f6afb9abcd25e99c5aa78c4d975895d +size 14257 diff --git a/Source/Editor/Modules/SimulationModule.cs b/Source/Editor/Modules/SimulationModule.cs index bad94846f..0994ce9f0 100644 --- a/Source/Editor/Modules/SimulationModule.cs +++ b/Source/Editor/Modules/SimulationModule.cs @@ -231,7 +231,7 @@ namespace FlaxEditor.Modules if (_isPlayModeRequested) { // Check if editor has been compiled and scripting reloaded (there is no pending reload action) - if ((ScriptsBuilder.IsReady || !Editor.Options.Options.General.AutoReloadScriptsOnMainWindowFocus) && !Level.IsAnyActionPending) + if ((ScriptsBuilder.IsReady || !Editor.Options.Options.General.AutoReloadScriptsOnMainWindowFocus) && !Level.IsAnyActionPending && Level.IsAnySceneLoaded) { // Clear flag _isPlayModeRequested = false; diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index b7621dbef..bdf9b82a4 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -1409,6 +1409,7 @@ namespace FlaxEditor.Viewport new ViewModeOptions(ViewMode.QuadOverdraw, "Quad Overdraw"), new ViewModeOptions(ViewMode.GlobalSDF, "Global SDF"), new ViewModeOptions(ViewMode.GlobalSurfaceAtlas, "Global Surface Atlas"), + new ViewModeOptions(ViewMode.GlobalIllumination, "Global Illumination"), }; private void WidgetCamSpeedShowHide(Control cm) diff --git a/Source/Engine/Content/Assets/Material.cpp b/Source/Engine/Content/Assets/Material.cpp index 97679193f..fba44b6f2 100644 --- a/Source/Engine/Content/Assets/Material.cpp +++ b/Source/Engine/Content/Assets/Material.cpp @@ -481,7 +481,7 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options) BytesContainer Material::LoadSurface(bool createDefaultIfMissing) { BytesContainer result; - if (WaitForLoaded()) + if (WaitForLoaded() && !LastLoadFailed()) return result; ScopeLock lock(Locker); diff --git a/Source/Engine/Core/Config/GraphicsSettings.cpp b/Source/Engine/Core/Config/GraphicsSettings.cpp index 75f302701..d10df48d2 100644 --- a/Source/Engine/Core/Config/GraphicsSettings.cpp +++ b/Source/Engine/Core/Config/GraphicsSettings.cpp @@ -15,6 +15,5 @@ void GraphicsSettings::Apply() Graphics::AllowCSMBlending = AllowCSMBlending; Graphics::GlobalSDFQuality = GlobalSDFQuality; Graphics::GIQuality = GIQuality; - Graphics::GlobalSurfaceAtlasResolution = GlobalSurfaceAtlasResolution; Graphics::PostProcessSettings = PostProcessSettings; } diff --git a/Source/Engine/Core/Config/GraphicsSettings.h b/Source/Engine/Core/Config/GraphicsSettings.h index 920a3cc0f..2ce028c95 100644 --- a/Source/Engine/Core/Config/GraphicsSettings.h +++ b/Source/Engine/Core/Config/GraphicsSettings.h @@ -89,6 +89,12 @@ public: API_FIELD(Attributes="EditorOrder(2100), DefaultValue(Quality.High), EditorDisplay(\"Global Illumination\")") Quality GIQuality = Quality::High; + /// + /// The Global Illumination probes spacing distance (in world units). Defines the quality of the GI resolution. Adjust to 200-500 to improve performance and lower frequency GI data. + /// + API_FIELD(Attributes="EditorOrder(2120), Limit(50, 1000), EditorDisplay(\"Global Illumination\")") + float GIProbesSpacing = 100; + /// /// The Global Surface Atlas resolution. Adjust it if atlas `flickers` due to overflow (eg. to 4096). /// diff --git a/Source/Engine/Core/Math/Math.h b/Source/Engine/Core/Math/Math.h index 40a1fa835..46e85247f 100644 --- a/Source/Engine/Core/Math/Math.h +++ b/Source/Engine/Core/Math/Math.h @@ -435,6 +435,14 @@ namespace Math return amount <= 0 ? 0 : amount >= 1 ? 1 : amount * amount * amount * (amount * (amount * 6 - 15) + 10); } + // Determines whether the specified value is close to zero (0.0) + // @param a The integer value + // @returns True if the specified value is close to zero (0.0). otherwise false + inline int32 IsZero(int32 a) + { + return a == 0; + } + // Determines whether the specified value is close to zero (0.0f) // @param a The floating value // @returns True if the specified value is close to zero (0.0f). otherwise false @@ -443,6 +451,14 @@ namespace Math return Abs(a) < ZeroTolerance; } + // Determines whether the specified value is close to one (1.0) + // @param a The integer value + // @returns True if the specified value is close to one (1.0). otherwise false + inline bool IsOne(int32 a) + { + return a == 1; + } + // Determines whether the specified value is close to one (1.0f) // @param a The floating value // @returns True if the specified value is close to one (1.0f). otherwise false diff --git a/Source/Engine/Graphics/Enums.h b/Source/Engine/Graphics/Enums.h index 56b1ca7bd..dd0154ce3 100644 --- a/Source/Engine/Graphics/Enums.h +++ b/Source/Engine/Graphics/Enums.h @@ -864,6 +864,11 @@ API_ENUM() enum class ViewMode /// Draw Global Surface Atlas preview. /// GlobalSurfaceAtlas = 25, + + /// + /// Draw Global Illumination debug preview (eg. irradiance probes). + /// + GlobalIllumination = 26, }; /// diff --git a/Source/Engine/Graphics/Graphics.cpp b/Source/Engine/Graphics/Graphics.cpp index 0f0a60c2f..aaa5dc2d8 100644 --- a/Source/Engine/Graphics/Graphics.cpp +++ b/Source/Engine/Graphics/Graphics.cpp @@ -16,7 +16,6 @@ Quality Graphics::ShadowMapsQuality = Quality::Medium; bool Graphics::AllowCSMBlending = false; Quality Graphics::GlobalSDFQuality = Quality::High; Quality Graphics::GIQuality = Quality::High; -int32 Graphics::GlobalSurfaceAtlasResolution = 2048; PostProcessSettings Graphics::PostProcessSettings; #if GRAPHICS_API_NULL diff --git a/Source/Engine/Graphics/Graphics.h b/Source/Engine/Graphics/Graphics.h index a1aed898c..c5933cfc1 100644 --- a/Source/Engine/Graphics/Graphics.h +++ b/Source/Engine/Graphics/Graphics.h @@ -63,11 +63,6 @@ public: /// API_FIELD() static Quality GIQuality; - /// - /// The Global Surface Atlas resolution. Adjust it if atlas `flickers` due to overflow. - /// - API_FIELD() static int32 GlobalSurfaceAtlasResolution; - /// /// The default Post Process settings. Can be overriden by PostFxVolume on a level locally, per camera or for a whole map. /// diff --git a/Source/Engine/Graphics/Materials/MaterialParams.cpp b/Source/Engine/Graphics/Materials/MaterialParams.cpp index 762ed1bb9..790aa6588 100644 --- a/Source/Engine/Graphics/Materials/MaterialParams.cpp +++ b/Source/Engine/Graphics/Materials/MaterialParams.cpp @@ -502,7 +502,7 @@ void MaterialParameter::Bind(BindMeta& meta) const GlobalSignDistanceFieldPass::BindingData bindingData; if (GlobalSignDistanceFieldPass::Instance()->Get(meta.Buffers, bindingData)) Platform::MemoryClear(&bindingData, sizeof(bindingData)); - bindingData.BindCascades(meta.Context, _registerIndex); + meta.Context->BindSR(_registerIndex, bindingData.Texture ? bindingData.Texture->ViewVolume() : nullptr); *((GlobalSignDistanceFieldPass::ConstantsData*)(meta.Constants.Get() + _offset)) = bindingData.Constants; break; } diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index 3f7a53d48..4a3524c2a 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -333,7 +333,7 @@ API_STRUCT() struct FLAXENGINE_API GlobalIlluminationSettings : ISerializable /// Defines how quickly GI blends between the the current frame and the history buffer. Lower values update GI faster, but with more jittering and noise. If the camera in your game doesn't move much, we recommend values closer to 1. /// API_FIELD(Attributes="EditorOrder(20), Limit(0, 1), PostProcessSetting((int)GlobalIlluminationSettingsOverride.TemporalResponse)") - float TemporalResponse = 0.8f; + float TemporalResponse = 0.9f; /// /// Draw distance of the Global Illumination effect. Scene outside the range will use fallback irradiance. diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index 5fb3259b7..c78e2f109 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -74,7 +74,8 @@ GPUContextDX12::GPUContextDX12(GPUDeviceDX12* device, D3D12_COMMAND_LIST_TYPE ty , _isCompute(0) , _rtDirtyFlag(0) , _psDirtyFlag(0) - , _cbDirtyFlag(0) + , _cbGraphicsDirtyFlag(0) + , _cbComputeDirtyFlag(0) , _samplersDirtyFlag(0) , _rtDepth(nullptr) , _ibHandle(nullptr) @@ -214,7 +215,8 @@ void GPUContextDX12::Reset() // Setup initial state _currentState = nullptr; _rtDirtyFlag = false; - _cbDirtyFlag = false; + _cbGraphicsDirtyFlag = false; + _cbComputeDirtyFlag = false; _samplersDirtyFlag = false; _rtCount = 0; _rtDepth = nullptr; @@ -453,19 +455,30 @@ void GPUContextDX12::flushUAVs() void GPUContextDX12::flushCBs() { - if (!_cbDirtyFlag) - return; - _cbDirtyFlag = false; - for (uint32 slotIndex = 0; slotIndex < ARRAY_COUNT(_cbHandles); slotIndex++) + if (_cbGraphicsDirtyFlag && !_isCompute) { - auto cb = _cbHandles[slotIndex]; - if (cb) + _cbGraphicsDirtyFlag = false; + for (uint32 i = 0; i < ARRAY_COUNT(_cbHandles); i++) { - ASSERT(cb->GPUAddress != 0); - if (_isCompute) - _commandList->SetComputeRootConstantBufferView(slotIndex, cb->GPUAddress); - else - _commandList->SetGraphicsRootConstantBufferView(slotIndex, cb->GPUAddress); + const auto cb = _cbHandles[i]; + if (cb) + { + ASSERT(cb->GPUAddress != 0); + _commandList->SetGraphicsRootConstantBufferView(i, cb->GPUAddress); + } + } + } + else if (_cbComputeDirtyFlag && _isCompute) + { + _cbComputeDirtyFlag = false; + for (uint32 i = 0; i < ARRAY_COUNT(_cbHandles); i++) + { + const auto cb = _cbHandles[i]; + if (cb) + { + ASSERT(cb->GPUAddress != 0); + _commandList->SetComputeRootConstantBufferView(i, cb->GPUAddress); + } } } } @@ -762,7 +775,7 @@ void GPUContextDX12::ClearUA(GPUTexture* texture, const Float4& value) SetResourceState(texDX12, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); flushRBs(); - + auto uav = ((GPUTextureViewDX12*)(texDX12->IsVolume() ? texDX12->ViewVolume() : texDX12->View(0)))->UAV(); Descriptor desc; GetActiveHeapDescriptor(uav, desc); @@ -867,7 +880,8 @@ void GPUContextDX12::ResetUA() void GPUContextDX12::ResetCB() { - _cbDirtyFlag = false; + _cbGraphicsDirtyFlag = false; + _cbComputeDirtyFlag = false; Platform::MemoryClear(_cbHandles, sizeof(_cbHandles)); } @@ -877,7 +891,8 @@ void GPUContextDX12::BindCB(int32 slot, GPUConstantBuffer* cb) auto cbDX12 = static_cast(cb); if (_cbHandles[slot] != cbDX12) { - _cbDirtyFlag = true; + _cbGraphicsDirtyFlag = true; + _cbComputeDirtyFlag = true; _cbHandles[slot] = cbDX12; } } @@ -988,7 +1003,8 @@ void GPUContextDX12::UpdateCB(GPUConstantBuffer* cb, const void* data) { if (_cbHandles[i] == cbDX12) { - _cbDirtyFlag = true; + _cbGraphicsDirtyFlag = true; + _cbComputeDirtyFlag = true; break; } } @@ -1296,7 +1312,7 @@ void GPUContextDX12::CopyResource(GPUResource* dstResource, GPUResource* srcReso { _commandList->CopyResource(dstResourceDX12->GetResource(), srcResourceDX12->GetResource()); } - // Texture -> Texture + // Texture -> Texture else if (srcType == GPUResource::ObjectType::Texture && dstType == GPUResource::ObjectType::Texture) { auto dstTextureDX12 = static_cast(dstResource); @@ -1388,7 +1404,7 @@ void GPUContextDX12::CopySubresource(GPUResource* dstResource, uint32 dstSubreso uint64 bytesCount = srcResource->GetMemoryUsage(); _commandList->CopyBufferRegion(dstBufferDX12->GetResource(), 0, srcBufferDX12->GetResource(), 0, bytesCount); } - // Texture -> Texture + // Texture -> Texture else if (srcType == GPUResource::ObjectType::Texture && dstType == GPUResource::ObjectType::Texture) { auto dstTextureDX12 = static_cast(dstResource); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h index 86700c3d8..47a331192 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h @@ -51,7 +51,8 @@ private: int32 _isCompute : 1; int32 _rtDirtyFlag : 1; int32 _psDirtyFlag : 1; - int32 _cbDirtyFlag : 1; + int32 _cbGraphicsDirtyFlag : 1; + int32 _cbComputeDirtyFlag : 1; int32 _samplersDirtyFlag : 1; GPUTextureViewDX12* _rtDepth; diff --git a/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.h b/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.h index e12ff45a3..55c8e4415 100644 --- a/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.h +++ b/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.h @@ -389,8 +389,9 @@ inline void SetDebugObjectName(T* resource, const Char* data, UINT size) if (data && size > 0) resource->SetName(data); #else - char* ansi = (char*)Allocator::Allocate(size); + char* ansi = (char*)Allocator::Allocate(size + 1); StringUtils::ConvertUTF162ANSI(data, ansi, size); + ansi[size] ='\0'; SetDebugObjectName(resource, ansi, size); Allocator::Free(ansi); #endif diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index e128da84e..d543cefb1 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -452,6 +452,7 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des case VK_DESCRIPTOR_TYPE_SAMPLER: { // Sampler + ASSERT_LOW_LAYER(slot < GPU_MAX_SAMPLER_BINDED); const VkSampler sampler = _samplerHandles[slot]; ASSERT(sampler); needsWrite |= dsWriter.WriteSampler(descriptorIndex, sampler, index); @@ -460,6 +461,7 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: { // Shader Resource (Texture) + ASSERT_LOW_LAYER(slot < GPU_MAX_SR_BINDED); auto handle = (GPUTextureViewVulkan*)handles[slot]; if (!handle) { @@ -490,6 +492,7 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: { // Shader Resource (Buffer) + ASSERT_LOW_LAYER(slot < GPU_MAX_SR_BINDED); auto sr = handles[slot]; if (!sr) { @@ -505,6 +508,7 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: { // Unordered Access (Texture) + ASSERT_LOW_LAYER(slot < GPU_MAX_UA_BINDED); auto ua = handles[slot]; ASSERT(ua); VkImageView imageView; @@ -516,6 +520,7 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: { // Unordered Access (Buffer) + ASSERT_LOW_LAYER(slot < GPU_MAX_UA_BINDED); auto ua = handles[slot]; if (!ua) { @@ -531,6 +536,7 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { // Unordered Access (Buffer) + ASSERT_LOW_LAYER(slot < GPU_MAX_UA_BINDED); auto ua = handles[slot]; if (!ua) { @@ -546,6 +552,7 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: { // Constant Buffer + ASSERT_LOW_LAYER(slot < GPU_MAX_CB_BINDED); auto cb = handles[slot]; ASSERT(cb); VkBuffer buffer; diff --git a/Source/Engine/Level/Actors/DirectionalLight.cpp b/Source/Engine/Level/Actors/DirectionalLight.cpp index 59c0213a7..621a0faf0 100644 --- a/Source/Engine/Level/Actors/DirectionalLight.cpp +++ b/Source/Engine/Level/Actors/DirectionalLight.cpp @@ -42,6 +42,8 @@ void DirectionalLight::Draw(RenderContext& renderContext) data.ShadowsMode = ShadowsMode; data.CascadeCount = CascadeCount; data.ContactShadowsLength = ContactShadowsLength; + data.StaticFlags = GetStaticFlags(); + data.ID = GetID(); renderContext.List->DirectionalLights.Add(data); } } diff --git a/Source/Engine/Level/Actors/PointLight.cpp b/Source/Engine/Level/Actors/PointLight.cpp index af0d77e70..a995a71f5 100644 --- a/Source/Engine/Level/Actors/PointLight.cpp +++ b/Source/Engine/Level/Actors/PointLight.cpp @@ -134,6 +134,8 @@ void PointLight::Draw(RenderContext& renderContext) data.ContactShadowsLength = ContactShadowsLength; data.IndirectLightingIntensity = IndirectLightingIntensity; data.IESTexture = IESTexture ? IESTexture->GetTexture() : nullptr; + data.StaticFlags = GetStaticFlags(); + data.ID = GetID(); renderContext.List->PointLights.Add(data); } } diff --git a/Source/Engine/Level/Actors/SkyLight.cpp b/Source/Engine/Level/Actors/SkyLight.cpp index 2261b72bb..db38fafe1 100644 --- a/Source/Engine/Level/Actors/SkyLight.cpp +++ b/Source/Engine/Level/Actors/SkyLight.cpp @@ -116,6 +116,8 @@ void SkyLight::Draw(RenderContext& renderContext) data.IndirectLightingIntensity = IndirectLightingIntensity; data.Radius = GetScaledRadius(); data.Image = GetSource(); + data.StaticFlags = GetStaticFlags(); + data.ID = GetID(); renderContext.List->SkyLights.Add(data); } } diff --git a/Source/Engine/Level/Actors/SpotLight.cpp b/Source/Engine/Level/Actors/SpotLight.cpp index ad69b1502..344c25542 100644 --- a/Source/Engine/Level/Actors/SpotLight.cpp +++ b/Source/Engine/Level/Actors/SpotLight.cpp @@ -187,6 +187,8 @@ void SpotLight::Draw(RenderContext& renderContext) data.IESTexture = IESTexture ? IESTexture->GetTexture() : nullptr; Float3::Transform(Float3::Up, GetOrientation(), data.UpVector); data.OuterConeAngle = outerConeAngle; + data.StaticFlags = GetStaticFlags(); + data.ID = GetID(); renderContext.List->SpotLights.Add(data); } } diff --git a/Source/Engine/Particles/ParticleEmitter.cpp b/Source/Engine/Particles/ParticleEmitter.cpp index d24e5b341..332f95dbb 100644 --- a/Source/Engine/Particles/ParticleEmitter.cpp +++ b/Source/Engine/Particles/ParticleEmitter.cpp @@ -335,7 +335,7 @@ void ParticleEmitter::InitCompilationOptions(ShaderCompilationOptions& options) BytesContainer ParticleEmitter::LoadSurface(bool createDefaultIfMissing) { BytesContainer result; - if (WaitForLoaded()) + if (WaitForLoaded() && !LastLoadFailed()) return result; ScopeLock lock(Locker); diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index 76fed479b..f428aa28a 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -9,6 +9,7 @@ #include "Engine/Core/Math/Int3.h" #include "Engine/Core/Math/Matrix3x3.h" #include "Engine/Core/Math/Quaternion.h" +#include "Engine/Core/Config/GraphicsSettings.h" #include "Engine/Engine/Engine.h" #include "Engine/Content/Content.h" #include "Engine/Debug/DebugDraw.h" @@ -33,8 +34,7 @@ // This must match HLSL #define DDGI_TRACE_RAYS_PROBES_COUNT_LIMIT 4096 // Maximum amount of probes to update at once during rays tracing and blending -#define DDGI_TRACE_RAYS_GROUP_SIZE_X 32 -#define DDGI_TRACE_RAYS_LIMIT 512 // Limit of rays per-probe (runtime value can be smaller) +#define DDGI_TRACE_RAYS_LIMIT 256 // Limit of rays per-probe (runtime value can be smaller) #define DDGI_PROBE_RESOLUTION_IRRADIANCE 6 // Resolution (in texels) for probe irradiance data (excluding 1px padding on each side) #define DDGI_PROBE_RESOLUTION_DISTANCE 14 // Resolution (in texels) for probe distance data (excluding 1px padding on each side) #define DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE 8 @@ -49,6 +49,7 @@ PACK_STRUCT(struct Data0 Float2 Padding0; float ResetBlend; float TemporalTime; + Int4 ProbeScrollClears[4]; }); PACK_STRUCT(struct Data1 @@ -68,16 +69,14 @@ public: float ProbesSpacing = 0.0f; Int3 ProbeScrollOffsets; Int3 ProbeScrollDirections; - bool ProbeScrollClear[3]; + Int3 ProbeScrollClears; void Clear() { ProbesOrigin = Float3::Zero; ProbeScrollOffsets = Int3::Zero; ProbeScrollDirections = Int3::Zero; - ProbeScrollClear[0] = false; - ProbeScrollClear[1] = false; - ProbeScrollClear[2] = false; + ProbeScrollClears = Int3::Zero; } } Cascades[4]; @@ -88,6 +87,8 @@ public: GPUTexture* ProbesState = nullptr; // Probes state: (RGB: world-space offset, A: state) GPUTexture* ProbesIrradiance = nullptr; // Probes irradiance (RGB: sRGB color) GPUTexture* ProbesDistance = nullptr; // Probes distance (R: mean distance, G: mean distance^2) + GPUBuffer* ActiveProbes = nullptr; // List with indices of the active probes (built during probes classification to use indirect dispatches for probes updating), counter at 0 + GPUBuffer* UpdateProbesInitArgs = nullptr; // Indirect dispatch buffer for active-only probes updating (trace+blend) DynamicDiffuseGlobalIlluminationPass::BindingData Result; FORCE_INLINE void Release() @@ -96,6 +97,8 @@ public: RenderTargetPool::Release(ProbesState); RenderTargetPool::Release(ProbesIrradiance); RenderTargetPool::Release(ProbesDistance); + SAFE_DELETE_GPU_RESOURCE(ActiveProbes); + SAFE_DELETE_GPU_RESOURCE(UpdateProbesInitArgs); } ~DDGICustomBuffer() @@ -174,7 +177,11 @@ bool DynamicDiffuseGlobalIlluminationPass::setupResources() if (!_cb0 || !_cb1) return true; _csClassify = shader->GetCS("CS_Classify"); - _csTraceRays = shader->GetCS("CS_TraceRays"); + _csUpdateProbesInitArgs = shader->GetCS("CS_UpdateProbesInitArgs"); + _csTraceRays[0] = shader->GetCS("CS_TraceRays", 0); + _csTraceRays[1] = shader->GetCS("CS_TraceRays", 1); + _csTraceRays[2] = shader->GetCS("CS_TraceRays", 2); + _csTraceRays[3] = shader->GetCS("CS_TraceRays", 3); _csUpdateProbesIrradiance = shader->GetCS("CS_UpdateProbes", 0); _csUpdateProbesDistance = shader->GetCS("CS_UpdateProbes", 1); _csUpdateBordersIrradianceRow = shader->GetCS("CS_UpdateBorders", 0); @@ -201,7 +208,11 @@ void DynamicDiffuseGlobalIlluminationPass::OnShaderReloading(Asset* obj) { LastFrameShaderReload = Engine::FrameCount; _csClassify = nullptr; - _csTraceRays = nullptr; + _csUpdateProbesInitArgs = nullptr; + _csTraceRays[0] = nullptr; + _csTraceRays[1] = nullptr; + _csTraceRays[2] = nullptr; + _csTraceRays[3] = nullptr; _csUpdateProbesIrradiance = nullptr; _csUpdateProbesDistance = nullptr; _csUpdateBordersIrradianceRow = nullptr; @@ -221,7 +232,6 @@ void DynamicDiffuseGlobalIlluminationPass::Dispose() // Cleanup _cb0 = nullptr; _cb1 = nullptr; - _csTraceRays = nullptr; _shader = nullptr; SAFE_DELETE_GPU_RESOURCE(_psIndirectLighting); #if USE_EDITOR @@ -268,26 +278,32 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, // Setup options auto& settings = renderContext.List->Settings.GlobalIllumination; - // TODO: implement GI Quality to affect cascades update rate, probes spacing and rays count per probe - const float probesSpacing = 100.0f; // GI probes placement spacing nearby camera (for closest cascade; gets automatically reduced for further cascades) - switch (Graphics::GIQuality) + auto* graphicsSettings = GraphicsSettings::Get(); + const float probesSpacing = Math::Clamp(graphicsSettings->GIProbesSpacing, 10.0f, 1000.0f); // GI probes placement spacing nearby camera (for closest cascade; gets automatically reduced for further cascades) + int32 probeRaysCount; // Amount of rays to trace randomly around each probe + switch (Graphics::GIQuality) // Ensure to match CS_TraceRays permutations { case Quality::Low: + probeRaysCount = 96; break; case Quality::Medium: + probeRaysCount = 128; break; case Quality::High: + probeRaysCount = 192; break; case Quality::Ultra: - default: + probeRaysCount = 256; break; + default: + return true; } - bool debugProbes = false; // TODO: add debug option to draw probes locations -> in Graphics window - Editor-only + ASSERT_LOW_LAYER(probeRaysCount <= DDGI_TRACE_RAYS_LIMIT); + bool debugProbes = renderContext.View.Mode == ViewMode::GlobalIllumination; const float indirectLightingIntensity = settings.Intensity; const float probeHistoryWeight = Math::Clamp(settings.TemporalResponse, 0.0f, 0.98f); const float distance = settings.Distance; const Color fallbackIrradiance = settings.FallbackIrradiance; - const int32 probeRaysCount = Math::Min(Math::AlignUp(256, DDGI_TRACE_RAYS_GROUP_SIZE_X), DDGI_TRACE_RAYS_LIMIT); // TODO: make it based on the GI Quality // Automatically calculate amount of cascades to cover the GI distance at the current probes spacing const int32 idealProbesCount = 20; // Ideal amount of probes per-cascade to try to fit in order to cover whole distance @@ -298,7 +314,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, idealDistance *= 2; cascadesCount++; } - + // Calculate the probes count based on the amount of cascades and the distance to cover const float cascadesDistanceScales[] = { 1.0f, 3.0f, 6.0f, 10.0f }; // Scales each cascade further away from the camera origin const float distanceExtent = distance / cascadesDistanceScales[cascadesCount - 1]; @@ -365,14 +381,19 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, // Allocate probes textures uint64 memUsage = 0; auto desc = GPUTextureDescription::New2D(probesCountTotalX, probesCountTotalY, PixelFormat::Unknown); - // TODO rethink probes data placement in memory -> what if we get [50x50x30] resolution? That's 75000 probes! Use sparse storage with active-only probes #define INIT_TEXTURE(texture, format, width, height) desc.Format = format; desc.Width = width; desc.Height = height; ddgiData.texture = RenderTargetPool::Get(desc); if (!ddgiData.texture) return true; memUsage += ddgiData.texture->GetMemoryUsage() desc.Flags = GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess; INIT_TEXTURE(ProbesTrace, PixelFormat::R16G16B16A16_Float, probeRaysCount, Math::Min(probesCountCascade, DDGI_TRACE_RAYS_PROBES_COUNT_LIMIT)); - INIT_TEXTURE(ProbesState, PixelFormat::R16G16B16A16_Float, probesCountTotalX, probesCountTotalY); // TODO: optimize to a RGBA32 (pos offset can be normalized to [0-0.5] range of ProbesSpacing and packed with state flag) + INIT_TEXTURE(ProbesState, PixelFormat::R8G8B8A8_SNorm, probesCountTotalX, probesCountTotalY); INIT_TEXTURE(ProbesIrradiance, PixelFormat::R11G11B10_Float, probesCountTotalX * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2), probesCountTotalY * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2)); INIT_TEXTURE(ProbesDistance, PixelFormat::R16G16_Float, probesCountTotalX * (DDGI_PROBE_RESOLUTION_DISTANCE + 2), probesCountTotalY * (DDGI_PROBE_RESOLUTION_DISTANCE + 2)); #undef INIT_TEXTURE +#define INIT_BUFFER(buffer, name) ddgiData.buffer = GPUDevice::Instance->CreateBuffer(TEXT(name)); if (!ddgiData.buffer || ddgiData.buffer->Init(desc2)) return true; memUsage += ddgiData.buffer->GetMemoryUsage(); + GPUBufferDescription desc2 = GPUBufferDescription::Raw((probesCountCascade + 1) * sizeof(uint32), GPUBufferFlags::ShaderResource | GPUBufferFlags::UnorderedAccess); + INIT_BUFFER(ActiveProbes, "DDGI.ActiveProbes"); + desc2 = GPUBufferDescription::Buffer(sizeof(GPUDispatchIndirectArgs) * Math::DivideAndRoundUp(probesCountCascade, DDGI_TRACE_RAYS_PROBES_COUNT_LIMIT), GPUBufferFlags::Argument | GPUBufferFlags::UnorderedAccess, PixelFormat::R32_UInt, nullptr, sizeof(uint32)); + INIT_BUFFER(UpdateProbesInitArgs, "DDGI.UpdateProbesInitArgs"); +#undef INIT_BUFFER LOG(Info, "Dynamic Diffuse Global Illumination memory usage: {0} MB, probes: {1}", memUsage / 1024 / 1024, probesCountTotal); clear = true; } @@ -389,8 +410,8 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, } // Calculate which cascades should be updated this frame - const uint64 cascadeFrequencies[] = { 1, 2, 3, 5 }; - // TODO: prevent updating 2 cascades at once on Low quality + const uint64 cascadeFrequencies[] = { 2, 3, 5, 7 }; + //const uint64 cascadeFrequencies[] = { 1, 2, 3, 5 }; //const uint64 cascadeFrequencies[] = { 1, 1, 1, 1 }; bool cascadeSkipUpdate[4]; for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) @@ -405,7 +426,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, continue; auto& cascade = ddgiData.Cascades[cascadeIndex]; - // Reset the volume origin and scroll offsets for each axis + // Reset the volume origin and scroll offsets for each axis once it overflows for (int32 axis = 0; axis < 3; axis++) { if (cascade.ProbeScrollOffsets.Raw[axis] != 0 && (cascade.ProbeScrollOffsets.Raw[axis] % ddgiData.ProbeCounts.Raw[axis] == 0)) @@ -423,7 +444,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, const float value = translation.Raw[axis] / cascade.ProbesSpacing; const int32 scroll = value >= 0.0f ? (int32)Math::Floor(value) : (int32)Math::Ceil(value); cascade.ProbeScrollOffsets.Raw[axis] += scroll; - cascade.ProbeScrollClear[axis] = scroll != 0; + cascade.ProbeScrollClears.Raw[axis] = scroll; cascade.ProbeScrollDirections.Raw[axis] = translation.Raw[axis] >= 0.0f ? 1 : -1; } } @@ -437,13 +458,11 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) { auto& cascade = ddgiData.Cascades[cascadeIndex]; - int32 probeScrollClear = cascade.ProbeScrollClear[0] + cascade.ProbeScrollClear[1] * 2 + cascade.ProbeScrollClear[2] * 4; // Pack clear flags into bits ddgiData.Result.Constants.ProbesOriginAndSpacing[cascadeIndex] = Float4(cascade.ProbesOrigin, cascade.ProbesSpacing); - ddgiData.Result.Constants.ProbesScrollOffsets[cascadeIndex] = Int4(cascade.ProbeScrollOffsets, probeScrollClear); - ddgiData.Result.Constants.ProbeScrollDirections[cascadeIndex] = Int4(cascade.ProbeScrollDirections, 0); + ddgiData.Result.Constants.ProbesScrollOffsets[cascadeIndex] = Int4(cascade.ProbeScrollOffsets, 0); } ddgiData.Result.Constants.RayMaxDistance = 10000.0f; // TODO: adjust to match perf/quality ratio (make it based on Global SDF and Global Surface Atlas distance) - ddgiData.Result.Constants.ViewDir = renderContext.View.Direction; + ddgiData.Result.Constants.ViewPos = renderContext.View.Position; ddgiData.Result.Constants.RaysCount = probeRaysCount; ddgiData.Result.Constants.ProbeHistoryWeight = probeHistoryWeight; ddgiData.Result.Constants.IrradianceGamma = 5.0f; @@ -465,6 +484,11 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, data.GlobalSDF = bindingDataSDF.Constants; data.GlobalSurfaceAtlas = bindingDataSurfaceAtlas.Constants; data.ResetBlend = clear ? 1.0f : 0.0f; + for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) + { + auto& cascade = ddgiData.Cascades[cascadeIndex]; + data.ProbeScrollClears[cascadeIndex] = Int4(cascade.ProbeScrollClears, 0); + } if (renderContext.List->Settings.AntiAliasing.Mode == AntialiasingMode::TemporalAntialiasing) { // Use temporal offset in the dithering factor (gets cleaned out by TAA) @@ -482,26 +506,6 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, context->BindCB(0, _cb0); } - // Classify probes (activation/deactivation and relocation) - { - PROFILE_GPU_CPU("Probes Classification"); - uint32 threadGroups = Math::DivideAndRoundUp(probesCountCascade, DDGI_PROBE_CLASSIFY_GROUP_SIZE); - bindingDataSDF.BindCascades(context, 0); - bindingDataSDF.BindCascadeMips(context, 4); - context->BindUA(0, ddgiData.Result.ProbesState); - for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) - { - if (cascadeSkipUpdate[cascadeIndex]) - continue; - Data1 data; - data.CascadeIndex = cascadeIndex; - context->UpdateCB(_cb1, &data); - context->BindCB(1, _cb1); - context->Dispatch(_csClassify, threadGroups, 1, 1); - } - context->ResetUA(); - } - // Update probes { PROFILE_GPU_CPU("Probes Update"); @@ -513,10 +517,38 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, continue; anyDirty = true; + // Classify probes (activation/deactivation and relocation) + { + PROFILE_GPU_CPU("Classify Probes"); + uint32 activeProbesCount = 0; + context->UpdateBuffer(ddgiData.ActiveProbes, &activeProbesCount, sizeof(uint32), 0); + threadGroupsX = Math::DivideAndRoundUp(probesCountCascade, DDGI_PROBE_CLASSIFY_GROUP_SIZE); + context->BindSR(0, bindingDataSDF.Texture ? bindingDataSDF.Texture->ViewVolume() : nullptr); + context->BindSR(1, bindingDataSDF.TextureMip ? bindingDataSDF.TextureMip->ViewVolume() : nullptr); + context->BindUA(0, ddgiData.Result.ProbesState); + context->BindUA(1, ddgiData.ActiveProbes->View()); + Data1 data; + data.CascadeIndex = cascadeIndex; + context->UpdateCB(_cb1, &data); + context->BindCB(1, _cb1); + context->Dispatch(_csClassify, threadGroupsX, 1, 1); + context->ResetUA(); + context->ResetSR(); + } + + // Build indirect args for probes updating (loop over active-only probes) + { + PROFILE_GPU_CPU("Init Args"); + context->BindSR(0, ddgiData.ActiveProbes->View()); + context->BindUA(0, ddgiData.UpdateProbesInitArgs->View()); + context->Dispatch(_csUpdateProbesInitArgs, 1, 1, 1); + context->ResetUA(); + } + // Update probes in batches so ProbesTrace texture can be smaller + uint32 arg = 0; for (int32 probesOffset = 0; probesOffset < probesCountCascade; probesOffset += DDGI_TRACE_RAYS_PROBES_COUNT_LIMIT) { - uint32 probesBatchSize = Math::Min(probesCountCascade - probesOffset, DDGI_TRACE_RAYS_PROBES_COUNT_LIMIT); Data1 data; data.CascadeIndex = cascadeIndex; data.ProbeIndexOffset = probesOffset; @@ -528,26 +560,20 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, PROFILE_GPU_CPU("Trace Rays"); // Global SDF with Global Surface Atlas software raytracing (thread X - per probe ray, thread Y - per probe) - ASSERT_LOW_LAYER((probeRaysCount % DDGI_TRACE_RAYS_GROUP_SIZE_X) == 0); - bindingDataSDF.BindCascades(context, 0); - bindingDataSDF.BindCascadeMips(context, 4); - context->BindSR(8, bindingDataSurfaceAtlas.Chunks ? bindingDataSurfaceAtlas.Chunks->View() : nullptr); - context->BindSR(9, bindingDataSurfaceAtlas.CulledObjects ? bindingDataSurfaceAtlas.CulledObjects->View() : nullptr); - context->BindSR(10, bindingDataSurfaceAtlas.AtlasDepth->View()); - context->BindSR(11, bindingDataSurfaceAtlas.AtlasLighting->View()); - context->BindSR(12, ddgiData.Result.ProbesState); - context->BindSR(13, skybox); + context->BindSR(0, bindingDataSDF.Texture ? bindingDataSDF.Texture->ViewVolume() : nullptr); + context->BindSR(1, bindingDataSDF.TextureMip ? bindingDataSDF.TextureMip->ViewVolume() : nullptr); + context->BindSR(2, bindingDataSurfaceAtlas.Chunks ? bindingDataSurfaceAtlas.Chunks->View() : nullptr); + context->BindSR(3, bindingDataSurfaceAtlas.CulledObjects ? bindingDataSurfaceAtlas.CulledObjects->View() : nullptr); + context->BindSR(4, bindingDataSurfaceAtlas.Objects ? bindingDataSurfaceAtlas.Objects->View() : nullptr); + context->BindSR(5, bindingDataSurfaceAtlas.AtlasDepth->View()); + context->BindSR(6, bindingDataSurfaceAtlas.AtlasLighting->View()); + context->BindSR(7, ddgiData.Result.ProbesState); + context->BindSR(8, skybox); + context->BindSR(9, ddgiData.ActiveProbes->View()); context->BindUA(0, ddgiData.ProbesTrace->View()); - context->Dispatch(_csTraceRays, probeRaysCount / DDGI_TRACE_RAYS_GROUP_SIZE_X, probesBatchSize, 1); + context->DispatchIndirect(_csTraceRays[(int32)Graphics::GIQuality], ddgiData.UpdateProbesInitArgs, arg); context->ResetUA(); context->ResetSR(); -#if 0 - // Probes trace debug preview - context->SetViewportAndScissors(renderContext.View.ScreenSize.X, renderContext.View.ScreenSize.Y); - context->SetRenderTarget(lightBuffer); - context->Draw(ddgiData.ProbesTrace); - return false; -#endif } // Update probes irradiance and distance textures (one thread-group per probe) @@ -555,11 +581,16 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, PROFILE_GPU_CPU("Update Probes"); context->BindSR(0, ddgiData.Result.ProbesState); context->BindSR(1, ddgiData.ProbesTrace->View()); + context->BindSR(2, ddgiData.ActiveProbes->View()); context->BindUA(0, ddgiData.Result.ProbesIrradiance); - context->Dispatch(_csUpdateProbesIrradiance, probesBatchSize, 1, 1); + context->DispatchIndirect(_csUpdateProbesIrradiance, ddgiData.UpdateProbesInitArgs, arg); context->BindUA(0, ddgiData.Result.ProbesDistance); - context->Dispatch(_csUpdateProbesDistance, probesBatchSize, 1, 1); + context->DispatchIndirect(_csUpdateProbesDistance, ddgiData.UpdateProbesInitArgs, arg); + context->ResetUA(); + context->ResetSR(); } + + arg += sizeof(GPUDispatchIndirectArgs); } } diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h index 5162fa6dc..af3d89083 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h @@ -17,7 +17,6 @@ public: { Float4 ProbesOriginAndSpacing[4]; Int4 ProbesScrollOffsets[4]; - Int4 ProbeScrollDirections[4]; uint32 ProbesCounts[3]; uint32 CascadesCount; float IrradianceGamma; @@ -25,7 +24,7 @@ public: float RayMaxDistance; float IndirectLightingIntensity; Float4 RaysRotation; - Float3 ViewDir; + Float3 ViewPos; uint32 RaysCount; Float3 FallbackIrradiance; float Padding0; @@ -46,7 +45,8 @@ private: GPUConstantBuffer* _cb0 = nullptr; GPUConstantBuffer* _cb1 = nullptr; GPUShaderProgramCS* _csClassify; - GPUShaderProgramCS* _csTraceRays; + GPUShaderProgramCS* _csUpdateProbesInitArgs; + GPUShaderProgramCS* _csTraceRays[4]; GPUShaderProgramCS* _csUpdateProbesIrradiance; GPUShaderProgramCS* _csUpdateProbesDistance; GPUShaderProgramCS* _csUpdateBordersIrradianceRow; diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp index b13d070d2..619c17fcf 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp @@ -10,8 +10,8 @@ #include "Engine/Core/Math/OrientedBoundingBox.h" #include "Engine/Engine/Engine.h" #include "Engine/Content/Content.h" +#include "Engine/Core/Config/GraphicsSettings.h" #include "Engine/Graphics/GPUDevice.h" -#include "Engine/Graphics/Graphics.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/RenderBuffers.h" #include "Engine/Graphics/RenderTargetPool.h" @@ -34,7 +34,7 @@ #define GLOBAL_SURFACE_ATLAS_TILE_PROJ_PLANE_OFFSET 0.1f // Small offset to prevent clipping with the closest triangles (shifts near and far planes) #define GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_REDRAW_TILES 0 // Forces to redraw all object tiles every frame #define GLOBAL_SURFACE_ATLAS_DEBUG_DRAW_OBJECTS 0 // Debug draws object bounds on redraw (and tile draw projection locations) -#define GLOBAL_SURFACE_ATLAS_DEBUG_DRAW_CHUNKS 0 // Debug draws culled chunks bounds (non-empty +#define GLOBAL_SURFACE_ATLAS_DEBUG_DRAW_CHUNKS 0 // Debug draws culled chunks bounds (non-empty) #if GLOBAL_SURFACE_ATLAS_DEBUG_DRAW_OBJECTS || GLOBAL_SURFACE_ATLAS_DEBUG_DRAW_CHUNKS #include "Engine/Debug/DebugDraw.h" @@ -86,7 +86,8 @@ struct GlobalSurfaceAtlasTile : RectPack struct GlobalSurfaceAtlasObject { uint64 LastFrameUsed; - uint64 LastFrameDirty; + uint64 LastFrameUpdated; + uint64 LightingUpdateFrame; // Index of the frame to update lighting for this object (calculated when object gets dirty or overriden by dynamic lights) Actor* Actor; GlobalSurfaceAtlasTile* Tiles[6]; float Radius; @@ -120,6 +121,12 @@ struct GlobalSurfaceAtlasObject } }; +struct GlobalSurfaceAtlasLight +{ + uint64 LastFrameUsed = 0; + uint64 LastFrameUpdated = 0; +}; + class GlobalSurfaceAtlasCustomBuffer : public RenderBuffers::CustomBuffer, public ISceneRenderingListener { public: @@ -134,10 +141,12 @@ public: GPUTexture* AtlasLighting = nullptr; GPUBuffer* ChunksBuffer = nullptr; GPUBuffer* CulledObjectsBuffer = nullptr; + DynamicTypedBuffer ObjectsBuffer; int32 CulledObjectsCounterIndex = -1; GlobalSurfaceAtlasPass::BindingData Result; GlobalSurfaceAtlasTile* AtlasTiles = nullptr; // TODO: optimize with a single allocation for atlas tiles Dictionary Objects; + Dictionary Lights; // Cached data to be reused during RasterizeActor uint64 CurrentFrame; @@ -148,12 +157,18 @@ public: float DistanceScalingEnd; float DistanceScaling; + GlobalSurfaceAtlasCustomBuffer() + : ObjectsBuffer(256 * (GLOBAL_SURFACE_ATLAS_OBJECT_DATA_STRIDE + GLOBAL_SURFACE_ATLAS_TILE_DATA_STRIDE * 3 / 4), PixelFormat::R32G32B32A32_Float, false, TEXT("GlobalSurfaceAtlas.ObjectsBuffer")) + { + } + FORCE_INLINE void ClearObjects() { CulledObjectsCounterIndex = -1; LastFrameAtlasDefragmentation = Engine::FrameCount; SAFE_DELETE(AtlasTiles); Objects.Clear(); + Lights.Clear(); } FORCE_INLINE void Clear() @@ -188,7 +203,13 @@ public: if (object) { // Dirty object to redraw - object->LastFrameDirty = 0; + object->LastFrameUpdated = 0; + } + GlobalSurfaceAtlasLight* light = Lights.TryGet(a->GetID()); + if (light) + { + // Dirty light to redraw + light->LastFrameUpdated = 0; } } } @@ -265,12 +286,20 @@ bool GlobalSurfaceAtlasPass::setupResources() if (_psClear->Init(psDesc)) return true; } + psDesc.DepthTestEnable = false; + psDesc.DepthWriteEnable = false; + psDesc.DepthFunc = ComparisonFunc::Never; + if (!_psClearLighting) + { + _psClearLighting = device->CreatePipelineState(); + psDesc.VS = shader->GetVS("VS_Atlas"); + psDesc.PS = shader->GetPS("PS_ClearLighting"); + if (_psClearLighting->Init(psDesc)) + return true; + } if (!_psDirectLighting0) { _psDirectLighting0 = device->CreatePipelineState(); - psDesc.DepthTestEnable = false; - psDesc.DepthWriteEnable = false; - psDesc.DepthFunc = ComparisonFunc::Never; psDesc.BlendMode = BlendingMode::Add; psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::RGB; psDesc.PS = shader->GetPS("PS_Lighting", 0); @@ -294,6 +323,7 @@ bool GlobalSurfaceAtlasPass::setupResources() void GlobalSurfaceAtlasPass::OnShaderReloading(Asset* obj) { SAFE_DELETE_GPU_RESOURCE(_psClear); + SAFE_DELETE_GPU_RESOURCE(_psClearLighting); SAFE_DELETE_GPU_RESOURCE(_psDirectLighting0); SAFE_DELETE_GPU_RESOURCE(_psDirectLighting1); SAFE_DELETE_GPU_RESOURCE(_psIndirectLighting); @@ -309,9 +339,9 @@ void GlobalSurfaceAtlasPass::Dispose() // Cleanup SAFE_DELETE(_vertexBuffer); - SAFE_DELETE(_objectsBuffer); SAFE_DELETE_GPU_RESOURCE(_culledObjectsSizeBuffer); SAFE_DELETE_GPU_RESOURCE(_psClear); + SAFE_DELETE_GPU_RESOURCE(_psClearLighting); SAFE_DELETE_GPU_RESOURCE(_psDirectLighting0); SAFE_DELETE_GPU_RESOURCE(_psDirectLighting1); SAFE_DELETE_GPU_RESOURCE(_psIndirectLighting); @@ -345,8 +375,9 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co PROFILE_GPU_CPU("Global Surface Atlas"); // Setup options - const int32 resolution = Math::Clamp(Graphics::GlobalSurfaceAtlasResolution, 256, GPU_MAX_TEXTURE_SIZE); - const float resolutionInv = 1.0f / resolution; + auto* graphicsSettings = GraphicsSettings::Get(); + const int32 resolution = Math::Clamp(graphicsSettings->GlobalSurfaceAtlasResolution, 256, GPU_MAX_TEXTURE_SIZE); + const float resolutionInv = 1.0f / (float)resolution; auto& giSettings = renderContext.List->Settings.GlobalIllumination; const float distance = giSettings.Distance; @@ -394,8 +425,6 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co surfaceAtlasData.AtlasTiles = New(0, 0, resolution, resolution); if (!_vertexBuffer) _vertexBuffer = New(0u, (uint32)sizeof(AtlasTileVertex), TEXT("GlobalSurfaceAtlas.VertexBuffer")); - if (!_objectsBuffer) - _objectsBuffer = New(256 * (GLOBAL_SURFACE_ATLAS_OBJECT_DATA_STRIDE + GLOBAL_SURFACE_ATLAS_TILE_DATA_STRIDE * 3 / 4), PixelFormat::R32G32B32A32_Float, false, TEXT("GlobalSurfaceAtlas.ObjectsBuffer")); // Utility for writing into tiles vertex buffer const Float2 posToClipMul(2.0f * resolutionInv, -2.0f * resolutionInv); @@ -430,7 +459,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co // Add objects into the atlas { PROFILE_CPU_NAMED("Draw"); - _objectsBuffer->Clear(); + surfaceAtlasData.ObjectsBuffer.Clear(); _dirtyObjectsBuffer.Clear(); _surfaceAtlasData = &surfaceAtlasData; renderContext.View.Pass = DrawPass::GlobalSurfaceAtlas; @@ -589,7 +618,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co // Send objects data to the GPU { PROFILE_GPU_CPU("Update Objects"); - _objectsBuffer->Flush(context); + surfaceAtlasData.ObjectsBuffer.Flush(context); } // Init constants @@ -607,10 +636,10 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co // Each chunk (ChunksBuffer) contains uint with address of the culled objects data start in CulledObjectsBuffer. // If chunk has address=0 then it's unused/empty. // Chunk [0,0,0] is unused and it's address=0 is used for atomic counter for writing into CulledObjectsBuffer. - // Each chunk data contains objects count + all objects with tiles copied into buffer. - // This allows to quickly convert world-space position into chunk, then read chunk data start and loop over culled objects (less objects and data already in place). + // Each chunk data contains objects count + all objects addresses. + // This allows to quickly convert world-space position into chunk, then read chunk data start and loop over culled objects. PROFILE_GPU_CPU("Cull Objects"); - uint32 objectsBufferCapacity = (uint32)((float)_objectsBuffer->Data.Count() * 1.3f); + uint32 objectsBufferCapacity = (uint32)((float)surfaceAtlasData.Objects.Count() * 1.3f); // Copy counter from ChunksBuffer into staging buffer to access current chunks memory usage to adapt dynamically to the scene complexity if (surfaceAtlasData.ChunksBuffer) @@ -634,7 +663,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co _culledObjectsSizeBuffer->Unmap(); if (counter > 0) { - objectsBufferCapacity = counter * sizeof(Float4); + objectsBufferCapacity = counter; notReady = false; } } @@ -652,28 +681,28 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co } } } - if (surfaceAtlasData.CulledObjectsCounterIndex != -1) + if (surfaceAtlasData.CulledObjectsCounterIndex != -1 && surfaceAtlasData.CulledObjectsBuffer) { // Copy current counter value _culledObjectsSizeFrames[surfaceAtlasData.CulledObjectsCounterIndex] = currentFrame; - context->CopyBuffer(_culledObjectsSizeBuffer, surfaceAtlasData.ChunksBuffer, sizeof(uint32), surfaceAtlasData.CulledObjectsCounterIndex * sizeof(uint32), 0); + context->CopyBuffer(_culledObjectsSizeBuffer, surfaceAtlasData.CulledObjectsBuffer, sizeof(uint32), surfaceAtlasData.CulledObjectsCounterIndex * sizeof(uint32), 0); } } // Allocate buffer for culled objects (estimated size) - objectsBufferCapacity = Math::Min(Math::AlignUp(objectsBufferCapacity, 4096u), (uint32)MAX_int32); + objectsBufferCapacity = Math::Min(Math::AlignUp(objectsBufferCapacity * sizeof(uint32), 4096u), (uint32)MAX_int32); if (!surfaceAtlasData.CulledObjectsBuffer) surfaceAtlasData.CulledObjectsBuffer = GPUDevice::Instance->CreateBuffer(TEXT("GlobalSurfaceAtlas.CulledObjectsBuffer")); if (surfaceAtlasData.CulledObjectsBuffer->GetSize() < objectsBufferCapacity) { - const GPUBufferDescription desc = GPUBufferDescription::Buffer(objectsBufferCapacity, GPUBufferFlags::UnorderedAccess | GPUBufferFlags::ShaderResource, PixelFormat::R32G32B32A32_Float, nullptr, sizeof(Float4)); + const auto desc = GPUBufferDescription::Raw(objectsBufferCapacity, GPUBufferFlags::UnorderedAccess | GPUBufferFlags::ShaderResource); if (surfaceAtlasData.CulledObjectsBuffer->Init(desc)) return true; } - // Clear chunks counter (chunk at 0 is used for a counter so chunks buffer is aligned) - uint32 counter = 1; // Indicate that 1st float4 is used so value 0 can be used as invalid chunk address - context->UpdateBuffer(surfaceAtlasData.ChunksBuffer, &counter, sizeof(counter), 0); + // Clear chunks counter (uint at 0 is used for a counter) + uint32 counter = 1; // Move write location for culled objects after counter + context->UpdateBuffer(surfaceAtlasData.CulledObjectsBuffer, &counter, sizeof(counter), 0); // Cull objects into chunks (1 thread per chunk) Data0 data; @@ -686,7 +715,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co context->BindCB(0, _cb0); static_assert(GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION % GLOBAL_SURFACE_ATLAS_CHUNKS_GROUP_SIZE == 0, "Invalid chunks resolution/groups setting."); const int32 chunkDispatchGroups = GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION / GLOBAL_SURFACE_ATLAS_CHUNKS_GROUP_SIZE; - context->BindSR(0, _objectsBuffer->GetBuffer()->View()); + context->BindSR(0, surfaceAtlasData.ObjectsBuffer.GetBuffer()->View()); context->BindUA(0, surfaceAtlasData.ChunksBuffer->View()); context->BindUA(1, surfaceAtlasData.CulledObjectsBuffer->View()); context->Dispatch(_csCullObjects, chunkDispatchGroups, chunkDispatchGroups, chunkDispatchGroups); @@ -700,11 +729,11 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co { for (int32 x = 0; x < GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION; x++) { - Float3 chunkCoord(x, y, z); - Float3 chunkMin = result.GlobalSurfaceAtlas.ViewPos + (chunkCoord - (GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION * 0.5f)) * result.GlobalSurfaceAtlas.ChunkSize; - Float3 chunkMax = chunkMin + result.GlobalSurfaceAtlas.ChunkSize; + Float3 chunkCoord((float)x, (float)y, (float)z); + Float3 chunkMin = result.Constants.ViewPos + (chunkCoord - (GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION * 0.5f)) * result.Constants.ChunkSize; + Float3 chunkMax = chunkMin + result.Constants.ChunkSize; BoundingBox chunkBounds(chunkMin, chunkMax); - if (Float3::Distance(chunkBounds.GetCenter(), result.GlobalSurfaceAtlas.ViewPos) >= 2000.0f) + if (Float3::Distance(chunkBounds.GetCenter(), result.Constants.ViewPos) >= 2000.0f) continue; int32 count = 0; @@ -733,37 +762,136 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co result.Atlas[4] = surfaceAtlasData.AtlasLighting; result.Chunks = surfaceAtlasData.ChunksBuffer; result.CulledObjects = surfaceAtlasData.CulledObjectsBuffer; + result.Objects = surfaceAtlasData.ObjectsBuffer.GetBuffer(); surfaceAtlasData.Result = result; // Render direct lighting into atlas if (surfaceAtlasData.Objects.Count() != 0) { PROFILE_GPU_CPU("Direct Lighting"); - - // Copy emissive light into the final direct lighting atlas - // TODO: test perf diff when manually copying only dirty object tiles and dirty light tiles together with indirect lighting - { - PROFILE_GPU_CPU("Copy Emissive"); - context->CopyTexture(surfaceAtlasData.AtlasLighting, 0, 0, 0, 0, surfaceAtlasData.AtlasEmissive, 0); - } - context->SetViewportAndScissors(Viewport(0, 0, (float)resolution, (float)resolution)); context->SetRenderTarget(surfaceAtlasData.AtlasLighting->View()); context->BindSR(0, surfaceAtlasData.AtlasGBuffer0->View()); context->BindSR(1, surfaceAtlasData.AtlasGBuffer1->View()); context->BindSR(2, surfaceAtlasData.AtlasGBuffer2->View()); context->BindSR(3, surfaceAtlasData.AtlasDepth->View()); - context->BindSR(4, _objectsBuffer->GetBuffer()->View()); - bindingDataSDF.BindCascades(context, 5); - bindingDataSDF.BindCascadeMips(context, 9); + context->BindSR(4, surfaceAtlasData.ObjectsBuffer.GetBuffer()->View()); + context->BindSR(5, bindingDataSDF.Texture ? bindingDataSDF.Texture->ViewVolume() : nullptr); + context->BindSR(6, bindingDataSDF.TextureMip ? bindingDataSDF.TextureMip->ViewVolume() : nullptr); context->BindCB(0, _cb0); Data0 data; data.ViewWorldPos = renderContext.View.Position; data.GlobalSDF = bindingDataSDF.Constants; data.GlobalSurfaceAtlas = result.Constants; + // Collect objects to update lighting this frame (dirty objects and dirty lights) + bool allLightingDirty = false; + for (auto& light : renderContext.List->DirectionalLights) + { + GlobalSurfaceAtlasLight& lightData = surfaceAtlasData.Lights[light.ID]; + lightData.LastFrameUsed = currentFrame; + uint32 redrawFramesCount = (light.StaticFlags & StaticFlags::Lightmap) ? 120 : 4; + if (surfaceAtlasData.CurrentFrame - lightData.LastFrameUpdated < (redrawFramesCount + (light.ID.D & redrawFramesCount))) + continue; + lightData.LastFrameUpdated = currentFrame; + + // Mark all objects to shade + allLightingDirty = true; + } + if (renderContext.View.Flags & ViewFlags::GI && (renderContext.List->DirectionalLights.Count() != 1 || renderContext.List->DirectionalLights[0].StaticFlags & StaticFlags::Lightmap)) + { + switch (renderContext.List->Settings.GlobalIllumination.Mode) + { + case GlobalIlluminationMode::DDGI: + { + DynamicDiffuseGlobalIlluminationPass::BindingData bindingDataDDGI; + if (!DynamicDiffuseGlobalIlluminationPass::Instance()->Get(renderContext.Buffers, bindingDataDDGI)) + { + GlobalSurfaceAtlasLight& lightData = surfaceAtlasData.Lights[Guid(0, 0, 0, 1)]; + lightData.LastFrameUsed = currentFrame; + uint32 redrawFramesCount = 4; // GI Bounce redraw minimum frequency + if (surfaceAtlasData.CurrentFrame - lightData.LastFrameUpdated < redrawFramesCount) + break; + lightData.LastFrameUpdated = currentFrame; + + // Mark all objects to shade + allLightingDirty = true; + } + break; + } + } + } + for (auto& light : renderContext.List->PointLights) + { + GlobalSurfaceAtlasLight& lightData = surfaceAtlasData.Lights[light.ID]; + lightData.LastFrameUsed = currentFrame; + uint32 redrawFramesCount = (light.StaticFlags & StaticFlags::Lightmap) ? 120 : 4; + if (surfaceAtlasData.CurrentFrame - lightData.LastFrameUpdated < (redrawFramesCount + (light.ID.D & redrawFramesCount))) + continue; + lightData.LastFrameUpdated = currentFrame; + + if (!allLightingDirty) + { + // Mark objects to shade + for (auto& e : surfaceAtlasData.Objects) + { + auto& object = e.Value; + Float3 lightToObject = object.Bounds.GetCenter() - light.Position; + if (lightToObject.LengthSquared() >= Math::Square(object.Radius + light.Radius)) + continue; + object.LightingUpdateFrame = currentFrame; + } + } + } + for (auto& light : renderContext.List->SpotLights) + { + GlobalSurfaceAtlasLight& lightData = surfaceAtlasData.Lights[light.ID]; + lightData.LastFrameUsed = currentFrame; + uint32 redrawFramesCount = (light.StaticFlags & StaticFlags::Lightmap) ? 120 : 4; + if (surfaceAtlasData.CurrentFrame - lightData.LastFrameUpdated < (redrawFramesCount + (light.ID.D & redrawFramesCount))) + continue; + lightData.LastFrameUpdated = currentFrame; + + if (!allLightingDirty) + { + // Mark objects to shade + for (auto& e : surfaceAtlasData.Objects) + { + auto& object = e.Value; + Float3 lightToObject = object.Bounds.GetCenter() - light.Position; + if (lightToObject.LengthSquared() >= Math::Square(object.Radius + light.Radius)) + continue; + object.LightingUpdateFrame = currentFrame; + } + } + } + + // Copy emissive light into the final direct lighting atlas + { + PROFILE_GPU_CPU("Copy Emissive"); + _vertexBuffer->Clear(); + for (const auto& e : surfaceAtlasData.Objects) + { + const auto& object = e.Value; + if (!allLightingDirty && object.LightingUpdateFrame != currentFrame) + continue; + for (int32 tileIndex = 0; tileIndex < 6; tileIndex++) + { + auto* tile = object.Tiles[tileIndex]; + if (!tile) + continue; + VB_WRITE_TILE(tile); + } + } + if (_vertexBuffer->Data.Count() != 0) + { + context->BindSR(7, surfaceAtlasData.AtlasEmissive); + context->SetState(_psClearLighting); + VB_DRAW(); + } + } + // Shade object tiles influenced by lights to calculate direct lighting - // TODO: reduce redraw frequency for static lights (StaticFlags::Lightmap) for (auto& light : renderContext.List->DirectionalLights) { // Collect tiles to shade @@ -771,6 +899,8 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co for (const auto& e : surfaceAtlasData.Objects) { const auto& object = e.Value; + if (!allLightingDirty && object.LightingUpdateFrame != currentFrame) + continue; for (int32 tileIndex = 0; tileIndex < 6; tileIndex++) { auto* tile = object.Tiles[tileIndex]; @@ -779,8 +909,11 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co VB_WRITE_TILE(tile); } } + if (_vertexBuffer->Data.Count() == 0) + continue; // Draw draw light + PROFILE_GPU_CPU("Directional Light"); const bool useShadow = CanRenderShadow(renderContext.View, light); // TODO: test perf/quality when using Shadow Map for directional light (ShadowsPass::Instance()->LastDirLightShadowMap) instead of Global SDF trace light.SetupLightData(&data.Light, useShadow); @@ -797,6 +930,8 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co for (const auto& e : surfaceAtlasData.Objects) { const auto& object = e.Value; + if (!allLightingDirty && object.LightingUpdateFrame != currentFrame) + continue; Float3 lightToObject = object.Bounds.GetCenter() - light.Position; if (lightToObject.LengthSquared() >= Math::Square(object.Radius + light.Radius)) continue; @@ -808,8 +943,11 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co VB_WRITE_TILE(tile); } } + if (_vertexBuffer->Data.Count() == 0) + continue; // Draw draw light + PROFILE_GPU_CPU("Point Light"); const bool useShadow = CanRenderShadow(renderContext.View, light); light.SetupLightData(&data.Light, useShadow); data.Light.Color *= light.IndirectLightingIntensity; @@ -825,6 +963,8 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co for (const auto& e : surfaceAtlasData.Objects) { const auto& object = e.Value; + if (!allLightingDirty && object.LightingUpdateFrame != currentFrame) + continue; Float3 lightToObject = object.Bounds.GetCenter() - light.Position; if (lightToObject.LengthSquared() >= Math::Square(object.Radius + light.Radius)) continue; @@ -836,8 +976,11 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co VB_WRITE_TILE(tile); } } + if (_vertexBuffer->Data.Count() == 0) + continue; // Draw draw light + PROFILE_GPU_CPU("Spot Light"); const bool useShadow = CanRenderShadow(renderContext.View, light); light.SetupLightData(&data.Light, useShadow); data.Light.Color *= light.IndirectLightingIntensity; @@ -846,9 +989,17 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co context->SetState(_psDirectLighting1); VB_DRAW(); } + + // Remove unused lights + for (auto it = surfaceAtlasData.Lights.Begin(); it.IsNotEnd(); ++it) + { + if (it->Value.LastFrameUsed != currentFrame) + surfaceAtlasData.Lights.Remove(it); + } + + // Draw draw indirect light from Global Illumination if (renderContext.View.Flags & ViewFlags::GI) { - // Draw draw indirect light from Global Illumination switch (renderContext.List->Settings.GlobalIllumination.Mode) { case GlobalIlluminationMode::DDGI: @@ -860,6 +1011,8 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co for (const auto& e : surfaceAtlasData.Objects) { const auto& object = e.Value; + if (!allLightingDirty && object.LightingUpdateFrame != currentFrame) + continue; for (int32 tileIndex = 0; tileIndex < 6; tileIndex++) { auto* tile = object.Tiles[tileIndex]; @@ -868,6 +1021,9 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co VB_WRITE_TILE(tile); } } + if (_vertexBuffer->Data.Count() == 0) + break; + PROFILE_GPU_CPU("DDGI"); data.DDGI = bindingDataDDGI.Constants; context->BindSR(5, bindingDataDDGI.ProbesState); context->BindSR(6, bindingDataDDGI.ProbesDistance); @@ -927,12 +1083,13 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex context->UpdateCB(_cb0, &data); context->BindCB(0, _cb0); } - bindingDataSDF.BindCascades(context, 0); - bindingDataSDF.BindCascadeMips(context, 4); - context->BindSR(8, bindingData.Chunks ? bindingData.Chunks->View() : nullptr); - context->BindSR(9, bindingData.CulledObjects ? bindingData.CulledObjects->View() : nullptr); - context->BindSR(10, bindingData.AtlasDepth->View()); - context->BindSR(12, skybox); + context->BindSR(0, bindingDataSDF.Texture ? bindingDataSDF.Texture->ViewVolume() : nullptr); + context->BindSR(1, bindingDataSDF.TextureMip ? bindingDataSDF.TextureMip->ViewVolume() : nullptr); + context->BindSR(2, bindingData.Chunks ? bindingData.Chunks->View() : nullptr); + context->BindSR(3, bindingData.CulledObjects ? bindingData.CulledObjects->View() : nullptr); + context->BindSR(4, bindingData.Objects ? bindingData.Objects->View() : nullptr); + context->BindSR(6, bindingData.AtlasDepth->View()); + context->BindSR(7, skybox); context->SetState(_psDebug); { Float2 outputSizeThird = outputSize * 0.333f; @@ -943,7 +1100,7 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex context->SetRenderTarget(tempBuffer->View()); // Full screen - direct light - context->BindSR(11, bindingData.AtlasLighting->View()); + context->BindSR(5, bindingData.AtlasLighting->View()); context->SetViewport(outputSize.X, outputSize.Y); context->SetScissor(Rectangle(0, 0, outputSizeTwoThird.X, outputSize.Y)); context->DrawFullscreenTriangle(); @@ -957,12 +1114,13 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex context->ResetRenderTarget(); // Rebind resources - bindingDataSDF.BindCascades(context, 0); - bindingDataSDF.BindCascadeMips(context, 4); - context->BindSR(8, bindingData.Chunks ? bindingData.Chunks->View() : nullptr); - context->BindSR(9, bindingData.CulledObjects ? bindingData.CulledObjects->View() : nullptr); - context->BindSR(10, bindingData.AtlasDepth->View()); - context->BindSR(12, skybox); + context->BindSR(0, bindingDataSDF.Texture ? bindingDataSDF.Texture->ViewVolume() : nullptr); + context->BindSR(1, bindingDataSDF.TextureMip ? bindingDataSDF.TextureMip->ViewVolume() : nullptr); + context->BindSR(2, bindingData.Chunks ? bindingData.Chunks->View() : nullptr); + context->BindSR(3, bindingData.CulledObjects ? bindingData.CulledObjects->View() : nullptr); + context->BindSR(4, bindingData.Objects ? bindingData.Objects->View() : nullptr); + context->BindSR(6, bindingData.AtlasDepth->View()); + context->BindSR(7, skybox); context->BindCB(0, _cb0); context->SetState(_psDebug); context->SetRenderTarget(output->View()); @@ -972,23 +1130,23 @@ void GlobalSurfaceAtlasPass::RenderDebug(RenderContext& renderContext, GPUContex context->UpdateCB(_cb0, &data); // Bottom left - diffuse - context->BindSR(11, bindingData.AtlasGBuffer0->View()); + context->BindSR(5, bindingData.AtlasGBuffer0->View()); context->SetViewportAndScissors(Viewport(outputSizeTwoThird.X, 0, outputSizeThird.X, outputSizeThird.Y)); context->DrawFullscreenTriangle(); // Bottom middle - normals - context->BindSR(11, bindingData.AtlasGBuffer1->View()); + context->BindSR(5, bindingData.AtlasGBuffer1->View()); context->SetViewportAndScissors(Viewport(outputSizeTwoThird.X, outputSizeThird.Y, outputSizeThird.X, outputSizeThird.Y)); context->DrawFullscreenTriangle(); // Bottom right - roughness/metalness/ao - context->BindSR(11, bindingData.AtlasGBuffer2->View()); + context->BindSR(5, bindingData.AtlasGBuffer2->View()); context->SetViewportAndScissors(Viewport(outputSizeTwoThird.X, outputSizeTwoThird.Y, outputSizeThird.X, outputSizeThird.Y)); context->DrawFullscreenTriangle(); } } -void GlobalSurfaceAtlasPass::RasterizeActor(Actor* actor, void* actorObject, const BoundingSphere& actorObjectBounds, const Matrix& localToWorld, const BoundingBox& localBounds, uint32 tilesMask) +void GlobalSurfaceAtlasPass::RasterizeActor(Actor* actor, void* actorObject, const BoundingSphere& actorObjectBounds, const Matrix& localToWorld, const BoundingBox& localBounds, uint32 tilesMask, bool useVisibility) { GlobalSurfaceAtlasCustomBuffer& surfaceAtlasData = *_surfaceAtlasData; Float3 boundsSize = localBounds.GetSize() * actor->GetScale(); @@ -1058,7 +1216,7 @@ void GlobalSurfaceAtlasPass::RasterizeActor(Actor* actor, void* actorObject, con // Redraw objects from time-to-time (dynamic objects can be animated, static objects can have textures streamed) uint32 redrawFramesCount = actor->HasStaticFlag(StaticFlags::Lightmap) ? 120 : 4; - if (surfaceAtlasData.CurrentFrame - object->LastFrameDirty >= (redrawFramesCount + (actor->GetID().D & redrawFramesCount))) + if (surfaceAtlasData.CurrentFrame - object->LastFrameUpdated >= (redrawFramesCount + (actor->GetID().D & redrawFramesCount))) dirty = true; // Mark object as used @@ -1069,7 +1227,8 @@ void GlobalSurfaceAtlasPass::RasterizeActor(Actor* actor, void* actorObject, con object->Radius = (float)actorObjectBounds.Radius; if (dirty || GLOBAL_SURFACE_ATLAS_DEBUG_FORCE_REDRAW_TILES) { - object->LastFrameDirty = surfaceAtlasData.CurrentFrame; + object->LastFrameUpdated = surfaceAtlasData.CurrentFrame; + object->LightingUpdateFrame = surfaceAtlasData.CurrentFrame; _dirtyObjectsBuffer.Add(actorObject); } @@ -1078,14 +1237,14 @@ void GlobalSurfaceAtlasPass::RasterizeActor(Actor* actor, void* actorObject, con object->Bounds.Transformation.GetWorld(localToWorldBounds); Matrix worldToLocalBounds; Matrix::Invert(localToWorldBounds, worldToLocalBounds); - uint32 objectAddress = _objectsBuffer->Data.Count() / sizeof(Float4); - auto* objectData = _objectsBuffer->WriteReserve(GLOBAL_SURFACE_ATLAS_OBJECT_DATA_STRIDE); + uint32 objectAddress = surfaceAtlasData.ObjectsBuffer.Data.Count() / sizeof(Float4); + auto* objectData = surfaceAtlasData.ObjectsBuffer.WriteReserve(GLOBAL_SURFACE_ATLAS_OBJECT_DATA_STRIDE); objectData[0] = *(Float4*)&actorObjectBounds; - objectData[1] = Float4::Zero; // w unused + objectData[1] = Float4::Zero; objectData[2] = Float4(worldToLocalBounds.M11, worldToLocalBounds.M12, worldToLocalBounds.M13, worldToLocalBounds.M41); objectData[3] = Float4(worldToLocalBounds.M21, worldToLocalBounds.M22, worldToLocalBounds.M23, worldToLocalBounds.M42); objectData[4] = Float4(worldToLocalBounds.M31, worldToLocalBounds.M32, worldToLocalBounds.M33, worldToLocalBounds.M43); - objectData[5] = Float4(object->Bounds.Extents, 0.0f); // w unused + objectData[5] = Float4(object->Bounds.Extents, useVisibility ? 1.0f : 0.0f); auto tileOffsets = reinterpret_cast(&objectData[1]); // xyz used for tile offsets packed into uint16 auto objectDataSize = reinterpret_cast(&objectData[1].W); // w used for object size (count of Float4s for object+tiles) *objectDataSize = GLOBAL_SURFACE_ATLAS_OBJECT_DATA_STRIDE; @@ -1130,7 +1289,7 @@ void GlobalSurfaceAtlasPass::RasterizeActor(Actor* actor, void* actorObject, con // Per-tile data const float tileWidth = (float)tile->Width - GLOBAL_SURFACE_ATLAS_TILE_PADDING; const float tileHeight = (float)tile->Height - GLOBAL_SURFACE_ATLAS_TILE_PADDING; - auto* tileData = _objectsBuffer->WriteReserve(GLOBAL_SURFACE_ATLAS_TILE_DATA_STRIDE); + auto* tileData = surfaceAtlasData.ObjectsBuffer.WriteReserve(GLOBAL_SURFACE_ATLAS_TILE_DATA_STRIDE); tileData[0] = Float4(tile->X, tile->Y, tileWidth, tileHeight) * surfaceAtlasData.ResolutionInv; tileData[1] = Float4(tile->ViewMatrix.M11, tile->ViewMatrix.M12, tile->ViewMatrix.M13, tile->ViewMatrix.M41); tileData[2] = Float4(tile->ViewMatrix.M21, tile->ViewMatrix.M22, tile->ViewMatrix.M23, tile->ViewMatrix.M42); diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.h b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.h index 394f3bb7a..793028977 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.h +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.h @@ -38,6 +38,7 @@ public: }; GPUBuffer* Chunks; GPUBuffer* CulledObjects; + GPUBuffer* Objects; ConstantsData Constants; }; @@ -45,6 +46,7 @@ private: bool _supported = false; AssetReference _shader; GPUPipelineState* _psClear = nullptr; + GPUPipelineState* _psClearLighting = nullptr; GPUPipelineState* _psDirectLighting0 = nullptr; GPUPipelineState* _psDirectLighting1 = nullptr; GPUPipelineState* _psIndirectLighting = nullptr; @@ -54,7 +56,6 @@ private: // Cache class GPUBuffer* _culledObjectsSizeBuffer = nullptr; - class DynamicTypedBuffer* _objectsBuffer = nullptr; class DynamicVertexBuffer* _vertexBuffer = nullptr; class GlobalSurfaceAtlasCustomBuffer* _surfaceAtlasData; Array _dirtyObjectsBuffer; @@ -79,7 +80,7 @@ public: void RenderDebug(RenderContext& renderContext, GPUContext* context, GPUTexture* output); // Rasterize actor into the Global Surface Atlas. Call it from actor Draw() method during DrawPass::GlobalSurfaceAtlas. - void RasterizeActor(Actor* actor, void* actorObject, const BoundingSphere& actorObjectBounds, const Matrix& localToWorld, const BoundingBox& localBounds, uint32 tilesMask = MAX_uint32); + void RasterizeActor(Actor* actor, void* actorObject, const BoundingSphere& actorObjectBounds, const Matrix& localToWorld, const BoundingBox& localBounds, uint32 tilesMask = MAX_uint32, bool useVisibility = true); private: #if COMPILE_WITH_DEV_ENV diff --git a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp index 4ef038efd..31f59d1c1 100644 --- a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp +++ b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp @@ -61,14 +61,18 @@ PACK_STRUCT(struct ModelsRasterizeData Int3 ChunkCoord; float MaxDistance; Float3 CascadeCoordToPosMul; - int ObjectsCount; + int32 ObjectsCount; Float3 CascadeCoordToPosAdd; int32 CascadeResolution; - float Padding0; + int32 CascadeIndex; float CascadeVoxelSize; int32 CascadeMipResolution; int32 CascadeMipFactor; uint32 Objects[GLOBAL_SDF_RASTERIZE_MODEL_MAX_COUNT]; + uint32 GenerateMipTexResolution; + uint32 GenerateMipCoordScale; + uint32 GenerateMipTexOffsetX; + uint32 GenerateMipMipOffsetX; }); struct RasterizeModel @@ -125,8 +129,6 @@ uint32 GetHash(const RasterizeChunkKey& key) struct CascadeData { - GPUTexture* Texture = nullptr; - GPUTexture* Mip = nullptr; Float3 Position; float VoxelSize; BoundingBox Bounds; @@ -163,18 +165,14 @@ struct CascadeData } } } - - ~CascadeData() - { - RenderTargetPool::Release(Texture); - RenderTargetPool::Release(Mip); - } }; class GlobalSignDistanceFieldCustomBuffer : public RenderBuffers::CustomBuffer, public ISceneRenderingListener { public: int32 Resolution = 0; + GPUTexture* Texture = nullptr; + GPUTexture* TextureMip = nullptr; Array> Cascades; HashSet ObjectTypes; HashSet SDFTextures; @@ -187,6 +185,8 @@ public: e.Item->Deleted.Unbind(this); e.Item->ResidentMipsChanged.Unbind(this); } + RenderTargetPool::Release(Texture); + RenderTargetPool::Release(TextureMip); } void OnSDFTextureDeleted(ScriptingObject* object) @@ -300,8 +300,7 @@ bool GlobalSignDistanceFieldPass::setupResources() _csRasterizeModel1 = shader->GetCS("CS_RasterizeModel", 1); _csRasterizeHeightfield = shader->GetCS("CS_RasterizeHeightfield"); _csClearChunk = shader->GetCS("CS_ClearChunk"); - _csGenerateMip0 = shader->GetCS("CS_GenerateMip", 0); - _csGenerateMip1 = shader->GetCS("CS_GenerateMip", 1); + _csGenerateMip = shader->GetCS("CS_GenerateMip"); // Init buffer if (!_objectsBuffer) @@ -329,8 +328,7 @@ void GlobalSignDistanceFieldPass::OnShaderReloading(Asset* obj) _csRasterizeModel1 = nullptr; _csRasterizeHeightfield = nullptr; _csClearChunk = nullptr; - _csGenerateMip0 = nullptr; - _csGenerateMip1 = nullptr; + _csGenerateMip = nullptr; _cb0 = nullptr; _cb1 = nullptr; invalidateResources(); @@ -351,18 +349,6 @@ void GlobalSignDistanceFieldPass::Dispose() ChunksCache.SetCapacity(0); } -void GlobalSignDistanceFieldPass::BindingData::BindCascades(GPUContext* context, int32 srvSlot) -{ - for (int32 i = 0; i < 4; i++) - context->BindSR(srvSlot + i, Cascades[i] ? Cascades[i]->ViewVolume() : nullptr); -} - -void GlobalSignDistanceFieldPass::BindingData::BindCascadeMips(GPUContext* context, int32 srvSlot) -{ - for (int32 i = 0; i < 4; i++) - context->BindSR(srvSlot + i, CascadeMips[i] ? CascadeMips[i]->ViewVolume() : nullptr); -} - bool GlobalSignDistanceFieldPass::Get(const RenderBuffers* buffers, BindingData& result) { auto* sdfData = buffers ? buffers->FindCustomBuffer(TEXT("GlobalSignDistanceField")) : nullptr; @@ -428,14 +414,13 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex sdfData.Cascades.Resize(cascadesCount); sdfData.Resolution = resolution; updated = true; - auto desc = GPUTextureDescription::New3D(resolution, resolution, resolution, GLOBAL_SDF_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess, 1); - for (auto& cascade : sdfData.Cascades) + auto desc = GPUTextureDescription::New3D(resolution * cascadesCount, resolution, resolution, GLOBAL_SDF_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess, 1); { - GPUTexture*& texture = cascade.Texture; + GPUTexture*& texture = sdfData.Texture; if (texture && texture->Width() != desc.Width) { RenderTargetPool::Release(texture); - texture = nullptr; + sdfData.Texture = nullptr; } if (!texture) { @@ -444,10 +429,11 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex return true; } } - desc.Width = desc.Height = desc.Depth = resolutionMip; + desc.Width = resolutionMip * cascadesCount; + desc.Height = desc.Depth = resolutionMip; for (auto& cascade : sdfData.Cascades) { - GPUTexture*& texture = cascade.Mip; + GPUTexture*& texture = sdfData.TextureMip; if (texture && texture->Width() != desc.Width) { RenderTargetPool::Release(texture); @@ -469,10 +455,12 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex { cascade.NonEmptyChunks.Clear(); cascade.StaticChunks.Clear(); - context->ClearUA(cascade.Texture, Float4::One); - context->ClearUA(cascade.Mip, Float4::One); } - LOG(Info, "Global SDF memory usage: {0} MB", (sdfData.Cascades[0].Texture->GetMemoryUsage() + sdfData.Cascades[0].Mip->GetMemoryUsage()) * ARRAY_COUNT(sdfData.Cascades) / 1024 / 1024); + uint64 memoryUsage = sdfData.Texture->GetMemoryUsage(); + context->ClearUA(sdfData.Texture, Float4::One); + memoryUsage += sdfData.TextureMip->GetMemoryUsage(); + context->ClearUA(sdfData.TextureMip, Float4::One); + LOG(Info, "Global SDF memory usage: {0} MB", memoryUsage / 1024 / 1024); } for (SceneRendering* scene : renderContext.List->Scenes) sdfData.ListenSceneRendering(scene); @@ -498,6 +486,8 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex bool anyDraw = false; const uint64 cascadeFrequencies[] = { 2, 3, 5, 11 }; //const uint64 cascadeFrequencies[] = { 1, 1, 1, 1 }; + GPUTextureView* textureView = sdfData.Texture->ViewVolume(); + GPUTextureView* textureMipView = sdfData.TextureMip->ViewVolume(); for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) { // Reduce frequency of the updates @@ -514,8 +504,6 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex BoundingBox cascadeBounds(center - cascadeDistance, center + cascadeDistance); // TODO: add scene detail scale factor to PostFx settings (eg. to increase or decrease scene details and quality) const float minObjectRadius = Math::Max(20.0f, cascadeVoxelSize * 0.5f); // Skip too small objects for this cascade - GPUTextureView* cascadeView = cascade.Texture->ViewVolume(); - GPUTextureView* cascadeMipView = cascade.Mip->ViewVolume(); // Clear cascade before rasterization { @@ -539,6 +527,8 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex _objectsBufferCount = 0; _voxelSize = cascadeVoxelSize; _cascadeBounds = cascadeBounds; + _cascadeBounds.Minimum += 0.1f; // Adjust to prevent overflowing chunk keys (cascade bounds are used for clamping object bounds) + _cascadeBounds.Maximum -= 0.1f; // Adjust to prevent overflowing chunk keys (cascade bounds are used for clamping object bounds) _cascadeIndex = cascadeIndex; _sdfData = &sdfData; { @@ -556,24 +546,18 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex } // Perform batched chunks rasterization - if (!anyDraw) - { - anyDraw = true; - context->ResetSR(); - auto desc = GPUTextureDescription::New3D(resolution, resolution, resolution, GLOBAL_SDF_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess, 1); - tmpMip = RenderTargetPool::Get(desc); - if (!tmpMip) - return true; - } + anyDraw = true; + context->ResetSR(); ModelsRasterizeData data; data.CascadeCoordToPosMul = (Float3)cascadeBounds.GetSize() / (float)resolution; data.CascadeCoordToPosAdd = (Float3)cascadeBounds.Minimum + cascadeVoxelSize * 0.5f; data.MaxDistance = cascadeMaxDistance; data.CascadeResolution = resolution; data.CascadeMipResolution = resolutionMip; + data.CascadeIndex = cascadeIndex; data.CascadeMipFactor = GLOBAL_SDF_RASTERIZE_MIP_FACTOR; data.CascadeVoxelSize = cascadeVoxelSize; - context->BindUA(0, cascadeView); + context->BindUA(0, textureView); context->BindCB(1, _cb1); const int32 chunkDispatchGroups = GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / GLOBAL_SDF_RASTERIZE_GROUP_SIZE; bool anyChunkDispatch = false; @@ -738,25 +722,55 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex if (updated || anyChunkDispatch) { PROFILE_GPU_CPU("Generate Mip"); - context->UpdateCB(_cb1, &data); context->ResetUA(); - context->BindSR(0, cascadeView); - context->BindUA(0, cascadeMipView); const int32 mipDispatchGroups = Math::DivideAndRoundUp(resolutionMip, GLOBAL_SDF_MIP_GROUP_SIZE); + static_assert((GLOBAL_SDF_MIP_FLOODS % 2) == 1, "Invalid Global SDF mip flood iterations count."); int32 floodFillIterations = chunks.Count() == 0 ? 1 : GLOBAL_SDF_MIP_FLOODS; - context->Dispatch(_csGenerateMip0, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups); - context->UnBindSR(0); + if (!tmpMip) + { + // Use temporary texture to flood fill mip + auto desc = GPUTextureDescription::New3D(resolutionMip, resolutionMip, resolutionMip, GLOBAL_SDF_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess, 1); + tmpMip = RenderTargetPool::Get(desc); + if (!tmpMip) + return true; + } GPUTextureView* tmpMipView = tmpMip->ViewVolume(); + + // Tex -> Mip + // TODO: use push constants on DX12/Vulkan to provide those 4 uints to the shader + data.GenerateMipTexResolution = data.CascadeResolution; + data.GenerateMipCoordScale = data.CascadeMipFactor; + data.GenerateMipTexOffsetX = data.CascadeIndex * data.CascadeResolution; + data.GenerateMipMipOffsetX = data.CascadeIndex * data.CascadeMipResolution; + context->UpdateCB(_cb1, &data); + context->BindSR(0, textureView); + context->BindUA(0, textureMipView); + context->Dispatch(_csGenerateMip, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups); + + data.GenerateMipTexResolution = data.CascadeMipResolution; + data.GenerateMipCoordScale = 1; for (int32 i = 1; i < floodFillIterations; i++) { context->ResetUA(); - context->BindSR(0, cascadeMipView); - context->BindUA(0, tmpMipView); - context->Dispatch(_csGenerateMip1, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups); - Swap(tmpMipView, cascadeMipView); + if ((i & 1) == 1) + { + // Mip -> Tmp + context->BindSR(0, textureMipView); + context->BindUA(0, tmpMipView); + data.GenerateMipTexOffsetX = data.CascadeIndex * data.CascadeMipResolution; + data.GenerateMipMipOffsetX = 0; + } + else + { + // Tmp -> Mip + context->BindSR(0, tmpMipView); + context->BindUA(0, textureMipView); + data.GenerateMipTexOffsetX = 0; + data.GenerateMipMipOffsetX = data.CascadeIndex * data.CascadeMipResolution; + } + context->UpdateCB(_cb1, &data); + context->Dispatch(_csGenerateMip, mipDispatchGroups, mipDispatchGroups, mipDispatchGroups); } - if (floodFillIterations % 2 == 0) - Swap(tmpMipView, cascadeMipView); } } @@ -771,26 +785,22 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex } // Copy results - static_assert(ARRAY_COUNT(result.Cascades) == ARRAY_COUNT(sdfData.Cascades), "Invalid cascades count."); - static_assert(ARRAY_COUNT(result.CascadeMips) == ARRAY_COUNT(sdfData.Cascades), "Invalid cascades count."); + result.Texture = sdfData.Texture; + result.TextureMip = sdfData.TextureMip; for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) { auto& cascade = sdfData.Cascades[cascadeIndex]; const float cascadeDistance = distanceExtent * cascadesDistanceScales[cascadeIndex]; const float cascadeMaxDistance = cascadeDistance * 2; - const float cascadeVoxelSize = cascadeMaxDistance / resolution; + const float cascadeVoxelSize = cascadeMaxDistance / (float)resolution; const Float3 center = cascade.Position; - result.Constants.CascadePosDistance[cascadeIndex] = Float4(center, cascadeDistance); + result.Constants.CascadePosDistance[cascadeIndex] = Vector4(center, cascadeDistance); result.Constants.CascadeVoxelSize.Raw[cascadeIndex] = cascadeVoxelSize; - result.Cascades[cascadeIndex] = cascade.Texture; - result.CascadeMips[cascadeIndex] = cascade.Mip; } for (int32 cascadeIndex = cascadesCount; cascadeIndex < 4; cascadeIndex++) { result.Constants.CascadePosDistance[cascadeIndex] = result.Constants.CascadePosDistance[cascadesCount - 1]; result.Constants.CascadeVoxelSize.Raw[cascadeIndex] = result.Constants.CascadeVoxelSize.Raw[cascadesCount - 1]; - result.Cascades[cascadeIndex] = nullptr; - result.CascadeMips[cascadeIndex] = nullptr; } result.Constants.Resolution = (float)resolution; result.Constants.CascadesCount = cascadesCount; @@ -820,8 +830,8 @@ void GlobalSignDistanceFieldPass::RenderDebug(RenderContext& renderContext, GPUC context->UpdateCB(_cb0, &data); context->BindCB(0, _cb0); } - bindingData.BindCascades(context, 0); - bindingData.BindCascadeMips(context, 4); + context->BindSR(0, bindingData.Texture ? bindingData.Texture->ViewVolume() : nullptr); + context->BindSR(1, bindingData.TextureMip ? bindingData.TextureMip->ViewVolume() : nullptr); context->SetState(_psDebug); context->SetRenderTarget(output->View()); context->SetViewportAndScissors(outputSize.X, outputSize.Y); diff --git a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h index 38e225ac8..3fbbb2776 100644 --- a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h +++ b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h @@ -23,12 +23,9 @@ public: // Binding data for the GPU. struct BindingData { - GPUTexture* Cascades[4]; - GPUTexture* CascadeMips[4]; + GPUTexture* Texture; + GPUTexture* TextureMip; ConstantsData Constants; - - void BindCascades(GPUContext* context, int32 srvSlot); - void BindCascadeMips(GPUContext* context, int32 srvSlot); }; private: @@ -39,8 +36,7 @@ private: GPUShaderProgramCS* _csRasterizeModel1 = nullptr; GPUShaderProgramCS* _csRasterizeHeightfield = nullptr; GPUShaderProgramCS* _csClearChunk = nullptr; - GPUShaderProgramCS* _csGenerateMip0 = nullptr; - GPUShaderProgramCS* _csGenerateMip1 = nullptr; + GPUShaderProgramCS* _csGenerateMip = nullptr; GPUConstantBuffer* _cb0 = nullptr; GPUConstantBuffer* _cb1 = nullptr; diff --git a/Source/Engine/Renderer/RenderList.h b/Source/Engine/Renderer/RenderList.h index 7401f77b5..7c81eacf4 100644 --- a/Source/Engine/Renderer/RenderList.h +++ b/Source/Engine/Renderer/RenderList.h @@ -34,6 +34,7 @@ struct RendererDirectionalLightData float ShadowsSharpness; float VolumetricScatteringIntensity; + StaticFlags StaticFlags; float IndirectLightingIntensity; int8 CastVolumetricShadow : 1; int8 RenderedVolumetricFog : 1; @@ -43,6 +44,8 @@ struct RendererDirectionalLightData float ContactShadowsLength; ShadowsCastingMode ShadowsMode; + Guid ID; + void SetupLightData(LightData* data, bool useShadow) const; }; @@ -76,11 +79,13 @@ struct RendererSpotLightData float IndirectLightingIntensity; ShadowsCastingMode ShadowsMode; + StaticFlags StaticFlags; int8 CastVolumetricShadow : 1; int8 RenderedVolumetricFog : 1; int8 UseInverseSquaredFalloff : 1; GPUTexture* IESTexture; + Guid ID; void SetupLightData(LightData* data, bool useShadow) const; }; @@ -111,11 +116,13 @@ struct RendererPointLightData float IndirectLightingIntensity; ShadowsCastingMode ShadowsMode; + StaticFlags StaticFlags; int8 CastVolumetricShadow : 1; int8 RenderedVolumetricFog : 1; int8 UseInverseSquaredFalloff : 1; GPUTexture* IESTexture; + Guid ID; void SetupLightData(LightData* data, bool useShadow) const; }; @@ -131,10 +138,12 @@ struct RendererSkyLightData Float3 AdditiveColor; float IndirectLightingIntensity; + StaticFlags StaticFlags; int8 CastVolumetricShadow : 1; int8 RenderedVolumetricFog : 1; CubeTexture* Image; + Guid ID; void SetupLightData(LightData* data, bool useShadow) const; }; @@ -211,7 +220,6 @@ struct DrawBatch class RenderListAllocation { public: - static FLAXENGINE_API void* Allocate(uintptr size); static FLAXENGINE_API void Free(void* ptr, uintptr size); @@ -222,7 +230,6 @@ public: uintptr _size; public: - FORCE_INLINE Data() { } @@ -329,7 +336,7 @@ struct DrawCallsList /// API_CLASS(Sealed) class FLAXENGINE_API RenderList : public ScriptingObject { -DECLARE_SCRIPTING_TYPE(RenderList); + DECLARE_SCRIPTING_TYPE(RenderList); /// /// Allocates the new renderer list object or reuses already allocated one. @@ -349,7 +356,6 @@ DECLARE_SCRIPTING_TYPE(RenderList); static void CleanupCache(); public: - /// /// All scenes for rendering. /// @@ -458,11 +464,9 @@ public: Float3 FrustumCornersVs[8]; private: - DynamicVertexBuffer _instanceBuffer; public: - /// /// Blends the postprocessing settings into the final options. /// @@ -527,7 +531,6 @@ public: } public: - /// /// Init cache for given task /// @@ -540,7 +543,6 @@ public: void Clear(); public: - /// /// Adds the draw call to the draw lists. /// diff --git a/Source/Engine/ShadersCompilation/Vulkan/ShaderCompilerVulkan.cpp b/Source/Engine/ShadersCompilation/Vulkan/ShaderCompilerVulkan.cpp index edf4a0887..fb66e78bd 100644 --- a/Source/Engine/ShadersCompilation/Vulkan/ShaderCompilerVulkan.cpp +++ b/Source/Engine/ShadersCompilation/Vulkan/ShaderCompilerVulkan.cpp @@ -7,6 +7,7 @@ #include "Engine/Platform/Platform.h" #include "Engine/Threading/Threading.h" #include "Engine/Serialization/MemoryWriteStream.h" +#include "Engine/Graphics/Config.h" #include "Engine/GraphicsDevice/Vulkan/Types.h" // Use glslang for HLSL to SPIR-V translation @@ -682,6 +683,10 @@ bool ShaderCompilerVulkan::CompileShader(ShaderFunctionMeta& meta, WritePermutat { auto& descriptor = descriptorsCollector.Descriptors[i]; + // Skip cases (eg. AppendStructuredBuffer counter buffer) + if (descriptor.Slot == MAX_uint16) + continue; + auto& d = header.DescriptorInfo.DescriptorTypes[header.DescriptorInfo.DescriptorTypesCount++]; d.Binding = descriptor.Binding; d.Set = stageSet; @@ -694,12 +699,15 @@ bool ShaderCompilerVulkan::CompileShader(ShaderFunctionMeta& meta, WritePermutat switch (descriptor.BindingType) { case SpirvShaderResourceBindingType::CB: + ASSERT_LOW_LAYER(descriptor.Slot >= 0 && descriptor.Slot < GPU_MAX_CB_BINDED); bindings.UsedCBsMask |= 1 << descriptor.Slot; break; case SpirvShaderResourceBindingType::SRV: + ASSERT_LOW_LAYER(descriptor.Slot >= 0 && descriptor.Slot < GPU_MAX_SR_BINDED); bindings.UsedSRsMask |= 1 << descriptor.Slot; break; case SpirvShaderResourceBindingType::UAV: + ASSERT_LOW_LAYER(descriptor.Slot >= 0 && descriptor.Slot < GPU_MAX_UA_BINDED); bindings.UsedUAsMask |= 1 << descriptor.Slot; break; } diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp index b1d80dd17..510ebdeda 100644 --- a/Source/Engine/Terrain/Terrain.cpp +++ b/Source/Engine/Terrain/Terrain.cpp @@ -542,7 +542,7 @@ void Terrain::Draw(RenderContext& renderContext) Matrix::Invert(chunk->GetWorld(), worldToLocal); BoundingBox::Transform(chunk->GetBounds(), worldToLocal, localBounds); BoundingSphere::FromBox(chunk->GetBounds(), chunkSphere); - GlobalSurfaceAtlasPass::Instance()->RasterizeActor(this, chunk, chunkSphere, chunk->GetWorld(), localBounds, 1 << 2); + GlobalSurfaceAtlasPass::Instance()->RasterizeActor(this, chunk, chunkSphere, chunk->GetWorld(), localBounds, 1 << 2, false); } } return; diff --git a/Source/Engine/Visject/ShaderGraphUtilities.cpp b/Source/Engine/Visject/ShaderGraphUtilities.cpp index d229d7c03..0cf15010c 100644 --- a/Source/Engine/Visject/ShaderGraphUtilities.cpp +++ b/Source/Engine/Visject/ShaderGraphUtilities.cpp @@ -170,8 +170,7 @@ const Char* ShaderGraphUtilities::GenerateShaderResources(TextWriterUnicode& wri format = TEXT("Texture3D {0} : register(t{1});"); break; case MaterialParameterType::GlobalSDF: - format = TEXT("Texture3D {0}_Tex[4] : register(t{1});"); - registers = 4; + format = TEXT("Texture3D {0}_Tex : register(t{1});"); zeroOffset = false; break; } diff --git a/Source/Shaders/Atmosphere.hlsl b/Source/Shaders/Atmosphere.hlsl index 8e1050bdb..751a2b480 100644 --- a/Source/Shaders/Atmosphere.hlsl +++ b/Source/Shaders/Atmosphere.hlsl @@ -37,7 +37,7 @@ const static float3 BetaRayleighScattering = float3(5.8e-3, 1.35e-2, 3.31e-2); / const static float HeightScaleMie = 1.2f; // 1.2 km, for Mie scattering const static float3 BetaMieScattering = float3(4e-3f, 4e-3f, 4e-3f); // Equation 4 const static float BetaRatio = 0.9f; // Figure 6, BetaMScattering/BetaMExtinction = 0.9 -const static float3 BetaMieExtinction = BetaMieScattering / BetaRatio.rrr; +const static float3 BetaMieExtinction = BetaMieScattering / BetaRatio.rrr; const static float MieG = 0.8f; // Equation 4 const static float RadiusScale = 1; @@ -83,16 +83,16 @@ Texture2D AtmosphereIrradianceTexture : register(t4); Texture3D AtmosphereInscatterTexture : register(t5); #else Texture2D AtmosphereTransmittanceTexture : register(t0); -Texture2D AtmosphereIrradianceTexture : register(t1); -Texture3D AtmosphereInscatterTexture : register(t2); +Texture2D AtmosphereIrradianceTexture : register(t1); +Texture3D AtmosphereInscatterTexture : register(t2); #endif -float2 GetTransmittanceUV(float radius, float Mu) +float2 GetTransmittanceUV(float radius, float Mu) { float u, v; #if TRANSMITTANCE_NON_LINEAR - v = sqrt((radius - RadiusGround) / (RadiusAtmosphere - RadiusGround)); - u = atan((Mu + 0.15) / (1.0 + 0.15) * tan(1.5)) / 1.5; + v = sqrt((radius - RadiusGround) / (RadiusAtmosphere - RadiusGround)); + u = atan((Mu + 0.15) / (1.0 + 0.15) * tan(1.5)) / 1.5; #else v = (radius - RadiusGround) / (RadiusAtmosphere - RadiusGround); u = (Mu + 0.15) / (1.0 + 0.15); @@ -100,7 +100,7 @@ float2 GetTransmittanceUV(float radius, float Mu) return float2(u, v); } -void GetTransmittanceRMuS(float2 uv, out float radius, out float MuS) +void GetTransmittanceRMuS(float2 uv, out float radius, out float MuS) { radius = uv.y; MuS = uv.x; @@ -113,14 +113,14 @@ void GetTransmittanceRMuS(float2 uv, out float radius, out float MuS) #endif } -float2 GetIrradianceUV(float radius, float MuS) +float2 GetIrradianceUV(float radius, float MuS) { float v = (radius - RadiusGround) / (RadiusAtmosphere - RadiusGround); float u = (MuS + 0.2) / (1.0 + 0.2); return float2(u, v); } -void GetIrradianceRMuS(float2 uv, out float radius, out float MuS) +void GetIrradianceRMuS(float2 uv, out float radius, out float MuS) { radius = RadiusGround + (uv.y * float(IrradianceTexHeight) - 0.5) / (float(IrradianceTexHeight) - 1.0) * (RadiusAtmosphere - RadiusGround); MuS = -0.2 + (uv.x * float(IrradianceTexWidth) - 0.5) / (float(IrradianceTexWidth) - 1.0) * (1.0 + 0.2); @@ -128,47 +128,47 @@ void GetIrradianceRMuS(float2 uv, out float radius, out float MuS) float4 Texture4DSample(Texture3D tex, float radius, float Mu, float MuS, float Nu) { - float H = sqrt(RadiusAtmosphere * RadiusAtmosphere - RadiusGround * RadiusGround); - float Rho = sqrt(radius * radius - RadiusGround * RadiusGround); + float H = sqrt(RadiusAtmosphere * RadiusAtmosphere - RadiusGround * RadiusGround); + float Rho = sqrt(radius * radius - RadiusGround * RadiusGround); #if INSCATTER_NON_LINEAR - float RMu = radius * Mu; - float Delta = RMu * RMu - radius * radius + RadiusGround * RadiusGround; - float4 TexOffset = RMu < 0.0 && Delta > 0.0 ? float4(1.0, 0.0, 0.0, 0.5 - 0.5 / float(InscatterMuNum)) : float4(-1.0, H * H, H, 0.5 + 0.5 / float(InscatterMuNum)); - float MuR = 0.5 / float(AtmosphericFogInscatterAltitudeSampleNum) + Rho / H * (1.0 - 1.0 / float(AtmosphericFogInscatterAltitudeSampleNum)); - float MuMu = TexOffset.w + (RMu * TexOffset.x + sqrt(Delta + TexOffset.y)) / (Rho + TexOffset.z) * (0.5 - 1.0 / float(InscatterMuNum)); - float MuMuS = 0.5 / float(InscatterMuSNum) + (atan(max(MuS, -0.1975) * tan(1.26 * 1.1)) / 1.1 + (1.0 - 0.26)) * 0.5 * (1.0 - 1.0 / float(InscatterMuSNum)); + float RMu = radius * Mu; + float Delta = RMu * RMu - radius * radius + RadiusGround * RadiusGround; + float4 TexOffset = RMu < 0.0 && Delta > 0.0 ? float4(1.0, 0.0, 0.0, 0.5 - 0.5 / float(InscatterMuNum)) : float4(-1.0, H * H, H, 0.5 + 0.5 / float(InscatterMuNum)); + float MuR = 0.5 / float(AtmosphericFogInscatterAltitudeSampleNum) + Rho / H * (1.0 - 1.0 / float(AtmosphericFogInscatterAltitudeSampleNum)); + float MuMu = TexOffset.w + (RMu * TexOffset.x + sqrt(Delta + TexOffset.y)) / (Rho + TexOffset.z) * (0.5 - 1.0 / float(InscatterMuNum)); + float MuMuS = 0.5 / float(InscatterMuSNum) + (atan(max(MuS, -0.1975) * tan(1.26 * 1.1)) / 1.1 + (1.0 - 0.26)) * 0.5 * (1.0 - 1.0 / float(InscatterMuSNum)); #else float MuR = 0.5 / float(AtmosphericFogInscatterAltitudeSampleNum) + Rho / H * (1.0 - 1.0 / float(AtmosphericFogInscatterAltitudeSampleNum)); float MuMu = 0.5 / float(InscatterMuNum) + (Mu + 1.0) * 0.5f * (1.0 - 1.0 / float(InscatterMuNum)); float MuMuS = 0.5 / float(InscatterMuSNum) + max(MuS + 0.2, 0.0) / 1.2 * (1.0 - 1.0 / float(InscatterMuSNum)); #endif - float LerpValue = (Nu + 1.0) * 0.5f * (float(InscatterNuNum) - 1.0); - float MuNu = floor(LerpValue); - LerpValue = LerpValue - MuNu; + float LerpValue = (Nu + 1.0) * 0.5f * (float(InscatterNuNum) - 1.0); + float MuNu = floor(LerpValue); + LerpValue = LerpValue - MuNu; - return tex.SampleLevel(SamplerLinearClamp, float3((MuNu + MuMuS) / float(InscatterNuNum), MuMu, MuR), 0) * (1.0 - LerpValue) - + tex.SampleLevel(SamplerLinearClamp, float3((MuNu + MuMuS + 1.0) / float(InscatterNuNum), MuMu, MuR), 0) * LerpValue; + return tex.SampleLevel(SamplerLinearClamp, float3((MuNu + MuMuS) / float(InscatterNuNum), MuMu, MuR), 0) * (1.0 - LerpValue) + + tex.SampleLevel(SamplerLinearClamp, float3((MuNu + MuMuS + 1.0) / float(InscatterNuNum), MuMu, MuR), 0) * LerpValue; } float Mod(float x, float y) { - return x - y * floor(x / y); + return x - y * floor(x / y); } -void GetMuMuSNu(float2 uv, float radius, float4 DhdH, out float Mu, out float MuS, out float Nu) +void GetMuMuSNu(float2 uv, float radius, float4 DhdH, out float Mu, out float MuS, out float Nu) { float x = uv.x * float(InscatterMuSNum * InscatterNuNum) - 0.5; float y = uv.y * float(InscatterMuNum) - 0.5; #if INSCATTER_NON_LINEAR - if (y < float(InscatterMuNum) * 0.5f) - { + if (y < float(InscatterMuNum) * 0.5f) + { float d = 1.0 - y / (float(InscatterMuNum) * 0.5f - 1.0); d = min(max(DhdH.z, d * DhdH.w), DhdH.w * 0.999); Mu = (RadiusGround * RadiusGround - radius * radius - d * d) / (2.0 * radius * d); Mu = min(Mu, -sqrt(1.0 - (RadiusGround / radius) * (RadiusGround / radius)) - 0.001); } - else - { + else + { float d = (y - float(InscatterMuNum) * 0.5f) / (float(InscatterMuNum) * 0.5f - 1.0); d = min(max(DhdH.x, d * DhdH.y), DhdH.y * 0.999); Mu = (RadiusAtmosphere * RadiusAtmosphere - radius * radius - d * d) / (2.0 * radius * d); @@ -185,15 +185,15 @@ void GetMuMuSNu(float2 uv, float radius, float4 DhdH, out float Mu, out float Mu } // Nearest intersection of ray r,mu with ground or top atmosphere boundary, mu=cos(ray zenith angle at ray origin) -float Limit(float radius, float Mu) +float Limit(float radius, float Mu) { float Dout = -radius * Mu + sqrt(radius * radius * (Mu * Mu - 1.0) + RadiusLimit * RadiusLimit); float Delta2 = radius * radius * (Mu * Mu - 1.0) + RadiusGround * RadiusGround; - if (Delta2 >= 0.0) - { + if (Delta2 >= 0.0) + { float Din = -radius * Mu - sqrt(Delta2); - if (Din >= 0.0) - { + if (Din >= 0.0) + { Dout = min(Dout, Din); } } @@ -201,90 +201,90 @@ float Limit(float radius, float Mu) } // Transmittance(=transparency) of atmosphere for infinite ray (r,mu) (mu=cos(view zenith angle)), intersections with ground ignored -float3 Transmittance(float radius, float Mu) +float3 Transmittance(float radius, float Mu) { - float2 uv = GetTransmittanceUV(radius, Mu); - return AtmosphereTransmittanceTexture.SampleLevel(SamplerLinearClamp, uv, 0).rgb; + float2 uv = GetTransmittanceUV(radius, Mu); + return AtmosphereTransmittanceTexture.SampleLevel(SamplerLinearClamp, uv, 0).rgb; } // Transmittance(=transparency) of atmosphere for infinite ray (r,mu) (mu=cos(view zenith angle)), or zero if ray intersects ground -float3 TransmittanceWithShadow(float radius, float Mu) +float3 TransmittanceWithShadow(float radius, float Mu) { - return Transmittance(radius, Mu); + return Transmittance(radius, Mu); } //Transmittance(=transparency) of atmosphere between x and x0. Assume segment x,x0 not intersecting ground. D = Distance between x and x0, mu=cos(zenith angle of [x,x0) ray at x) -float3 TransmittanceWithDistance(float radius, float Mu, float D) +float3 TransmittanceWithDistance(float radius, float Mu, float D) { float3 result; float R1 = sqrt(radius * radius + D * D + 2.0 * radius * Mu * D); float Mu1 = (radius * Mu + D) / R1; - if (Mu > 0.0) - { + if (Mu > 0.0) + { result = min(Transmittance(radius, Mu) / Transmittance(R1, Mu1), 1.0); } - else - { + else + { result = min(Transmittance(R1, -Mu1) / Transmittance(radius, -Mu), 1.0); } return result; } // Transmittance(=transparency) of atmosphere between x and x0. Assume segment x,x0 not intersecting ground radius=||x||, Mu=cos(zenith angle of [x,x0) ray at x), v=unit direction vector of [x,x0) ray -float3 TransmittanceWithDistance(float radius, float Mu, float3 V, float3 X0) +float3 TransmittanceWithDistance(float radius, float Mu, float3 V, float3 X0) { float3 result; float d1 = length(X0); float Mu1 = dot(X0, V) / radius; - if (Mu > 0.0) + if (Mu > 0.0) result = min(Transmittance(radius, Mu) / Transmittance(d1, Mu1), 1.0); - else + else result = min(Transmittance(d1, -Mu1) / Transmittance(radius, -Mu), 1.0); return result; } // Optical depth for ray (r,mu) of length d, using analytic formula (mu=cos(view zenith angle)), intersections with ground ignored H=height scale of exponential density function -float OpticalDepthWithDistance(float H, float radius, float Mu, float D) +float OpticalDepthWithDistance(float H, float radius, float Mu, float D) { - float particleDensity = 6.2831; // REK 04, Table 2 + float particleDensity = 6.2831; // REK 04, Table 2 float a = sqrt(0.5 / H * radius); float2 A01 = a * float2(Mu, Mu + D / radius); float2 A01Sign = sign(A01); - float2 A01Squared = A01*A01; + float2 A01Squared = A01 * A01; float x = A01Sign.y > A01Sign.x ? exp(A01Squared.x) : 0.0; - float2 y = A01Sign / (2.3193 * abs(A01) + sqrt(1.52 * A01Squared + 4.0)) * float2(1.0, exp(-D / H*(D / (2.0 * radius) + Mu))); - return sqrt((particleDensity * H)*radius) * exp((RadiusGround - radius) / H) * (x + dot(y, float2(1.0, -1.0))); + float2 y = A01Sign / (2.3193 * abs(A01) + sqrt(1.52 * A01Squared + 4.0)) * float2(1.0, exp(-D / H * (D / (2.0 * radius) + Mu))); + return sqrt((particleDensity * H) * radius) * exp((RadiusGround - radius) / H) * (x + dot(y, float2(1.0, -1.0))); } // Transmittance(=transparency) of atmosphere for ray (r,mu) of length d (mu=cos(view zenith angle)), intersections with ground ignored uses analytic formula instead of transmittance texture, REK 04, Atmospheric Transparency -float3 AnalyticTransmittance(float R, float Mu, float D) +float3 AnalyticTransmittance(float R, float Mu, float D) { return exp(- BetaRayleighScattering * OpticalDepthWithDistance(HeightScaleRayleigh, R, Mu, D) - BetaMieExtinction * OpticalDepthWithDistance(HeightScaleMie, R, Mu, D)); } -float3 Irradiance(Texture2D tex, float r, float muS) +float3 Irradiance(Texture2D tex, float r, float muS) { float2 uv = GetIrradianceUV(r, muS); - return tex.SampleLevel(SamplerLinearClamp, uv, 0).rgb; + return tex.SampleLevel(SamplerLinearClamp, uv, 0).rgb; } // Rayleigh phase function -float PhaseFunctionR(float Mu) +float PhaseFunctionR(float Mu) { return (3.0 / (16.0 * PI)) * (1.0 + Mu * Mu); } // Mie phase function -float PhaseFunctionM(float Mu) +float PhaseFunctionM(float Mu) { - return 1.5 * 1.0 / (4.0 * PI) * (1.0 - MieG * MieG) * pow(1.0 + (MieG * MieG) - 2.0 * MieG * Mu, -3.0/2.0) * (1.0 + Mu * Mu) / (2.0 + MieG * MieG); + return 1.5 * 1.0 / (4.0 * PI) * (1.0 - MieG * MieG) * pow(1.0 + (MieG * MieG) - 2.0 * MieG * Mu, -3.0 / 2.0) * (1.0 + Mu * Mu) / (2.0 + MieG * MieG); } // Approximated single Mie scattering (cf. approximate Cm in paragraph "Angular precision") -float3 GetMie(float4 RayMie) -{ - // RayMie.rgb=C*, RayMie.w=Cm,r - return RayMie.rgb * RayMie.w / max(RayMie.r, 1e-4) * (BetaRayleighScattering.rrr / BetaRayleighScattering.rgb); +float3 GetMie(float4 RayMie) +{ + // RayMie.rgb=C*, RayMie.w=Cm,r + return RayMie.rgb * RayMie.w / max(RayMie.r, 1e-4) * (BetaRayleighScattering.rrr / BetaRayleighScattering.rgb); } #endif diff --git a/Source/Shaders/AtmosphereFog.hlsl b/Source/Shaders/AtmosphereFog.hlsl index 45e000e11..ae60a25ef 100644 --- a/Source/Shaders/AtmosphereFog.hlsl +++ b/Source/Shaders/AtmosphereFog.hlsl @@ -27,222 +27,221 @@ static const float HeightOffset = 0.01f; // inscattered light along ray x+tv, when sun in direction s (=S[L]-T(x,x0)S[L]|x0) float3 GetInscatterColor(float fogDepth, float3 X, float T, float3 V, float3 S, float radius, float Mu, out float3 attenuation, bool isSceneGeometry) { - float3 result = float3(0.0f, 0.0f, 0.0f); - attenuation = float3(1.0f, 1.0f, 1.0f); + float3 result = float3(0.0f, 0.0f, 0.0f); + attenuation = float3(1.0f, 1.0f, 1.0f); - float d = -radius * Mu - sqrt(radius * radius * (Mu * Mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere); - if (d > 0.0f) - { - // if X in space and ray intersects atmosphere - // move X to nearest intersection of ray with top atmosphere boundary - X += d * V; - T -= d; - Mu = (radius * Mu + d) / RadiusAtmosphere; - radius = RadiusAtmosphere; - } + float d = -radius * Mu - sqrt(radius * radius * (Mu * Mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere); + if (d > 0.0f) + { + // if X in space and ray intersects atmosphere + // move X to nearest intersection of ray with top atmosphere boundary + X += d * V; + T -= d; + Mu = (radius * Mu + d) / RadiusAtmosphere; + radius = RadiusAtmosphere; + } - float epsilon = 0.005f; + float epsilon = 0.005f; - if (radius < RadiusGround + HeightOffset + epsilon) - { - float diff = (RadiusGround + HeightOffset + epsilon) - radius; - X -= diff * V; - T -= diff; - radius = RadiusGround + HeightOffset + epsilon; - Mu = dot(X, V) / radius; - } + if (radius < RadiusGround + HeightOffset + epsilon) + { + float diff = (RadiusGround + HeightOffset + epsilon) - radius; + X -= diff * V; + T -= diff; + radius = RadiusGround + HeightOffset + epsilon; + Mu = dot(X, V) / radius; + } - if (radius <= RadiusAtmosphere && fogDepth > 0.0f) - { - float3 X0 = X + T * V; - float R0 = length(X0); - float Nu = dot(V, S); - float MuS = dot(X, S) / radius; + if (radius <= RadiusAtmosphere && fogDepth > 0.0f) + { + float3 X0 = X + T * V; + float R0 = length(X0); + float Nu = dot(V, S); + float MuS = dot(X, S) / radius; - float MuHorizon = -sqrt(1.0 - (RadiusGround / radius) * (RadiusGround / radius)); + float MuHorizon = -sqrt(1.0 - (RadiusGround / radius) * (RadiusGround / radius)); - if (isSceneGeometry) - { - Mu = max(Mu, MuHorizon + epsilon + 0.15f); - } - else - { - Mu = max(Mu, MuHorizon + epsilon); - } + if (isSceneGeometry) + { + Mu = max(Mu, MuHorizon + epsilon + 0.15f); + } + else + { + Mu = max(Mu, MuHorizon + epsilon); + } - float MuOriginal = Mu; - float blendRatio = 0.0f; + float MuOriginal = Mu; + float blendRatio = 0.0f; - if (isSceneGeometry) - { - blendRatio = saturate(exp(-V.z) - 0.5); - if (blendRatio < 1.0) - { - V.z = max(V.z, 0.15); - V = normalize(V); - float3 x1 = X + T * V; - Mu = dot(x1, V) / length(x1); - } - } + if (isSceneGeometry) + { + blendRatio = saturate(exp(-V.z) - 0.5); + if (blendRatio < 1.0) + { + V.z = max(V.z, 0.15); + V = normalize(V); + float3 x1 = X + T * V; + Mu = dot(x1, V) / length(x1); + } + } - float phaseR = PhaseFunctionR(Nu); - float phaseM = PhaseFunctionM(Nu); - float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu), 0.0); + float phaseR = PhaseFunctionR(Nu); + float phaseM = PhaseFunctionM(Nu); + float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu), 0.0); - if (T > 0.0) - { - attenuation = AnalyticTransmittance(radius, Mu, T); - float Mu0 = dot(X0, V) / R0; - float MuS0 = dot(X0, S) / R0; - if (isSceneGeometry) - { - R0 = max(R0, radius); - } + if (T > 0.0) + { + attenuation = AnalyticTransmittance(radius, Mu, T); + float Mu0 = dot(X0, V) / R0; + float MuS0 = dot(X0, S) / R0; + if (isSceneGeometry) + { + R0 = max(R0, radius); + } - if (R0 > RadiusGround + HeightOffset) - { - if (blendRatio < 1.0) - { - inscatter = max(inscatter - attenuation.rgbr * Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu), 0.0); - if (!isSceneGeometry) - { - if (abs(Mu - MuHorizon) < epsilon) - { - Mu = MuHorizon - epsilon; - R0 = sqrt(radius * radius + T * T + 2.0 * radius * T * Mu); - Mu0 = (radius * Mu + T) / R0; + if (R0 > RadiusGround + HeightOffset) + { + if (blendRatio < 1.0) + { + inscatter = max(inscatter - attenuation.rgbr * Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu), 0.0); + if (!isSceneGeometry) + { + if (abs(Mu - MuHorizon) < epsilon) + { + Mu = MuHorizon - epsilon; + R0 = sqrt(radius * radius + T * T + 2.0 * radius * T * Mu); + Mu0 = (radius * Mu + T) / R0; - Mu0 = max(MuHorizon + epsilon, Mu0); - float4 inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu); - float4 inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu); - float4 inscatterA = max(inscatter0 - attenuation.rgbr * inscatter1, 0.0); + Mu0 = max(MuHorizon + epsilon, Mu0); + float4 inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu); + float4 inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu); + float4 inscatterA = max(inscatter0 - attenuation.rgbr * inscatter1, 0.0); - Mu = MuHorizon + epsilon; - R0 = sqrt(radius * radius + T * T + 2.0 * radius * T * Mu); + Mu = MuHorizon + epsilon; + R0 = sqrt(radius * radius + T * T + 2.0 * radius * T * Mu); - Mu0 = (radius * Mu + T) / R0; - Mu0 = max(MuHorizon + epsilon, Mu0); - inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu); - inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu); - float4 inscatterB = max(inscatter1 - attenuation.rgbr * inscatter1, 0.0); + Mu0 = (radius * Mu + T) / R0; + Mu0 = max(MuHorizon + epsilon, Mu0); + inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu); + inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu); + float4 inscatterB = max(inscatter1 - attenuation.rgbr * inscatter1, 0.0); - float alpha = ((Mu - MuHorizon) + epsilon) * 0.5f / epsilon; - inscatter = lerp(inscatterA, inscatterB, alpha); - } - } - else if (blendRatio > 0.0) - { - inscatter = lerp(inscatter, (1.0 - attenuation.rgbr) * max(Texture4DSample(AtmosphereInscatterTexture, radius, MuOriginal, MuS, Nu), 0.0), blendRatio); - } - } - else - { - inscatter = (1.0 - attenuation.rgbr) * inscatter; - } - } - } + float alpha = ((Mu - MuHorizon) + epsilon) * 0.5f / epsilon; + inscatter = lerp(inscatterA, inscatterB, alpha); + } + } + else if (blendRatio > 0.0) + { + inscatter = lerp(inscatter, (1.0 - attenuation.rgbr) * max(Texture4DSample(AtmosphereInscatterTexture, radius, MuOriginal, MuS, Nu), 0.0), blendRatio); + } + } + else + { + inscatter = (1.0 - attenuation.rgbr) * inscatter; + } + } + } - inscatter.w *= smoothstep(0.00, 0.02, MuS); - result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0); - } + inscatter.w *= smoothstep(0.00, 0.02, MuS); + result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0); + } - return result; + return result; } // Ground radiance at end of ray x+tv, when sun in direction s attenuated between ground and viewer (=R[L0]+R[L*]) float3 GetGroundColor(float4 sceneColor, float3 X, float T, float3 V, float3 S, float radius, float3 attenuation, bool isSceneGeometry) { - float3 result = float3(0.0f, 0.0f, 0.0f); - if (T > 0.0f) - { - // if ray hits ground surface - // ground Reflectance at end of ray, X0 - float3 X0 = X + T * V; - float R0 = length(X0); - float3 N = X0 / R0; - sceneColor.xyz = saturate(sceneColor.xyz + 0.05); + float3 result = float3(0.0f, 0.0f, 0.0f); + if (T > 0.0f) + { + // if ray hits ground surface + // ground Reflectance at end of ray, X0 + float3 X0 = X + T * V; + float R0 = length(X0); + float3 N = X0 / R0; + sceneColor.xyz = saturate(sceneColor.xyz + 0.05); - float4 reflectance = sceneColor * float4(0.2, 0.2, 0.2, 1.0); + float4 reflectance = sceneColor * float4(0.2, 0.2, 0.2, 1.0); - // direct sun light (radiance) reaching X0 - float MuS = dot(N, S); - float3 sunLight = isSceneGeometry ? float3(0.f, 0.f, 0.f) : TransmittanceWithShadow(R0, MuS); + // direct sun light (radiance) reaching X0 + float MuS = dot(N, S); + float3 sunLight = isSceneGeometry ? float3(0.f, 0.f, 0.f) : TransmittanceWithShadow(R0, MuS); - // precomputed sky light (irradiance) (=E[L*]) at X0 - float3 groundSkyLight = Irradiance(AtmosphereIrradianceTexture, R0, MuS); + // precomputed sky light (irradiance) (=E[L*]) at X0 + float3 groundSkyLight = Irradiance(AtmosphereIrradianceTexture, R0, MuS); - // light reflected at X0 (=(R[L0]+R[L*])/T(X,X0)) - float3 groundColor = reflectance.rgb * ((max(MuS, 0.0) * sunLight + groundSkyLight) / PI); + // light reflected at X0 (=(R[L0]+R[L*])/T(X,X0)) + float3 groundColor = reflectance.rgb * ((max(MuS, 0.0) * sunLight + groundSkyLight) / PI); - // water specular color due to SunLight - if (!isSceneGeometry && reflectance.w > 0.0) - { - float3 H = normalize(S - V); - float fresnel = 0.02 + 0.98 * pow(1.0 - dot(-V, H), 5.0); - float waterBrdf = fresnel * pow(max(dot(H, N), 0.0), 150.0); - groundColor += reflectance.w * max(waterBrdf, 0.0) * sunLight; - } + // water specular color due to SunLight + if (!isSceneGeometry && reflectance.w > 0.0) + { + float3 H = normalize(S - V); + float fresnel = 0.02 + 0.98 * pow(1.0 - dot(-V, H), 5.0); + float waterBrdf = fresnel * pow(max(dot(H, N), 0.0), 150.0); + groundColor += reflectance.w * max(waterBrdf, 0.0) * sunLight; + } - result = attenuation * groundColor; //=R[L0]+R[L*] - } - return result; + result = attenuation * groundColor; //=R[L0]+R[L*] + } + return result; } // Direct sun light for ray x+tv, when sun in direction s (=L0) float3 GetSunColor(AtmosphericFogData atmosphericFog, float3 X, float T, float3 V, float3 S, float radius, float Mu) { - if (T > 0.0) - return float3(0.0f, 0.0f, 0.0f); + if (T > 0.0) + return float3(0.0f, 0.0f, 0.0f); - float3 transmittance = radius <= RadiusAtmosphere ? TransmittanceWithShadow(radius, Mu) : float3(1.0, 1.0, 1.0); // T(X,xo) - float sunIntensity = step(cos(PI * atmosphericFog.AtmosphericFogSunDiscScale / 180.0), dot(V, S)); // Lsun - return transmittance * sunIntensity; // Eq (9) + float3 transmittance = radius <= RadiusAtmosphere ? TransmittanceWithShadow(radius, Mu) : float3(1.0, 1.0, 1.0); // T(X,xo) + float sunIntensity = step(cos(PI * atmosphericFog.AtmosphericFogSunDiscScale / 180.0), dot(V, S)); // Lsun + return transmittance * sunIntensity; // Eq (9) } float3 inscatter(inout float3 x, inout float t, float3 v, float3 s, out float r, out float mu, out float3 attenuation) { - float3 result = 0; - r = length(x); - mu = dot(x, v) / r; - float d = -r * mu - sqrt(r * r * (mu * mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere); + float3 result = 0; + r = length(x); + mu = dot(x, v) / r; + float d = -r * mu - sqrt(r * r * (mu * mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere); - // if x in space and ray intersects atmosphere - if (d > 0.0) - { - // move x to nearest intersection of ray with top atmosphere boundary - x += d * v; - t -= d; - mu = (r * mu + d) / RadiusAtmosphere; - r = RadiusAtmosphere; - } + // if x in space and ray intersects atmosphere + if (d > 0.0) + { + // move x to nearest intersection of ray with top atmosphere boundary + x += d * v; + t -= d; + mu = (r * mu + d) / RadiusAtmosphere; + r = RadiusAtmosphere; + } - float epsilon = 0.0045f; + float epsilon = 0.0045f; - // if ray intersects atmosphere - if (r <= RadiusAtmosphere) - { - float nu = dot(v, s); - float muS = dot(x, s) / r; - float phaseR = PhaseFunctionR(nu); - float phaseM = PhaseFunctionM(nu); - float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu), 0.0); + // if ray intersects atmosphere + if (r <= RadiusAtmosphere) + { + float nu = dot(v, s); + float muS = dot(x, s) / r; + float phaseR = PhaseFunctionR(nu); + float phaseM = PhaseFunctionM(nu); + float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu), 0.0); - if (t > 0.0) - { - float3 x0 = x + t * v; - float r0 = length(x0); - float rMu0 = dot(x0, v); - float mu0 = rMu0 / r0; - float muS0 = dot(x0, s) / r0; + if (t > 0.0) + { + float3 x0 = x + t * v; + float r0 = length(x0); + float rMu0 = dot(x0, v); + float mu0 = rMu0 / r0; + float muS0 = dot(x0, s) / r0; attenuation = AnalyticTransmittance(r, mu, t); - if (r0 > RadiusGround + 0.01) - { - // computes S[L]-T(x,x0)S[L]|x0 - inscatter = max(inscatter - attenuation.rgbr * Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu), 0.0); + if (r0 > RadiusGround + 0.01) + { + // computes S[L]-T(x,x0)S[L]|x0 + inscatter = max(inscatter - attenuation.rgbr * Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu), 0.0); float muHoriz = -sqrt(1.0 - (RadiusGround / r) * (RadiusGround / r)); if (abs(mu - muHoriz) < epsilon) - { - + { mu = muHoriz - epsilon; r0 = sqrt(r * r + t * t + 2.0 * r * t * mu); mu0 = (r * mu + t) / r0; @@ -256,18 +255,18 @@ float3 inscatter(inout float3 x, inout float t, float3 v, float3 s, out float r, inScatter0 = Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu); inScatter1 = Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu); float4 inScatterB = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0); - + float alpha = ((mu - muHoriz) + epsilon) / (2.0 * epsilon); inscatter = lerp(inScatterA, inScatterB, alpha); } - } - } + } + } inscatter.w *= smoothstep(0.00, 0.02, muS); - result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0); - } + result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0); + } - return result; + return result; } static const float EPSILON_ATMOSPHERE = 0.002f; @@ -280,40 +279,40 @@ static const float EPSILON_INSCATTER = 0.004f; // output - return value: intersection occurred true/false bool intersectAtmosphere(in float3 viewPosition, in float3 d, out float offset, out float maxPathLength) { - offset = 0.0f; - maxPathLength = 0.0f; + offset = 0.0f; + maxPathLength = 0.0f; - // vector from ray origin to center of the sphere - float3 l = -viewPosition; - float l2 = dot(l, l); - float s = dot(l, d); + // vector from ray origin to center of the sphere + float3 l = -viewPosition; + float l2 = dot(l, l); + float s = dot(l, d); - // adjust top atmosphere boundary by small epsilon to prevent artifacts - float r = Rt - EPSILON_ATMOSPHERE; - float r2 = r * r; - if (l2 <= r2) - { - // ray origin inside sphere, hit is ensured - float m2 = l2 - (s * s); - float q = sqrt(r2 - m2); - maxPathLength = s + q; - return true; - } - else if (s >= 0) - { - // ray starts outside in front of sphere, hit is possible - float m2 = l2 - (s * s); - if (m2 <= r2) - { - // ray hits atmosphere definitely - float q = sqrt(r2 - m2); - offset = s - q; - maxPathLength = (s + q) - offset; - return true; - } - } + // adjust top atmosphere boundary by small epsilon to prevent artifacts + float r = Rt - EPSILON_ATMOSPHERE; + float r2 = r * r; + if (l2 <= r2) + { + // ray origin inside sphere, hit is ensured + float m2 = l2 - (s * s); + float q = sqrt(r2 - m2); + maxPathLength = s + q; + return true; + } + if (s >= 0) + { + // ray starts outside in front of sphere, hit is possible + float m2 = l2 - (s * s); + if (m2 <= r2) + { + // ray hits atmosphere definitely + float q = sqrt(r2 - m2); + offset = s - q; + maxPathLength = (s + q) - offset; + return true; + } + } - return false; + return false; } // input - surfacePos: reconstructed position of current pixel @@ -323,105 +322,105 @@ bool intersectAtmosphere(in float3 viewPosition, in float3 d, out float offset, // output - return value: total in-scattered light float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosition, in float3 surfacePos, in float3 viewDir, in out float3 attenuation, in out float irradianceFactor) { - float3 inscatteredLight = float3(0.0f, 0.0f, 0.0f); - float offset; - float maxPathLength; + float3 inscatteredLight = float3(0.0f, 0.0f, 0.0f); + float offset; + float maxPathLength; - if (intersectAtmosphere(viewPosition, viewDir, offset, maxPathLength)) - { - return float3(offset / 10, 0, 0); + if (intersectAtmosphere(viewPosition, viewDir, offset, maxPathLength)) + { + return float3(offset / 10, 0, 0); - float pathLength = distance(viewPosition, surfacePos); - //return pathLength.xxx; + float pathLength = distance(viewPosition, surfacePos); + //return pathLength.xxx; - // check if object occludes atmosphere - if (pathLength > offset) - { - //return float3(1,0,0); + // check if object occludes atmosphere + if (pathLength > offset) + { + //return float3(1,0,0); - // offsetting camera - float3 startPos = viewPosition + offset * viewDir; - float startPosHeight = length(startPos); - pathLength -= offset; + // offsetting camera + float3 startPos = viewPosition + offset * viewDir; + float startPosHeight = length(startPos); + pathLength -= offset; - // starting position of path is now ensured to be inside atmosphere - // was either originally there or has been moved to top boundary - float muStartPos = dot(startPos, viewDir) / startPosHeight; - float nuStartPos = dot(viewDir, atmosphericFog.AtmosphericFogSunDirection); - float musStartPos = dot(startPos, atmosphericFog.AtmosphericFogSunDirection) / startPosHeight; + // starting position of path is now ensured to be inside atmosphere + // was either originally there or has been moved to top boundary + float muStartPos = dot(startPos, viewDir) / startPosHeight; + float nuStartPos = dot(viewDir, atmosphericFog.AtmosphericFogSunDirection); + float musStartPos = dot(startPos, atmosphericFog.AtmosphericFogSunDirection) / startPosHeight; - // in-scattering for infinite ray (light in-scattered when - // no surface hit or object behind atmosphere) - float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, startPosHeight, muStartPos, musStartPos, nuStartPos), 0.0f); - float surfacePosHeight = length(surfacePos); - float musEndPos = dot(surfacePos, atmosphericFog.AtmosphericFogSunDirection) / surfacePosHeight; - //return float3(muStartPos, 0, 0); + // in-scattering for infinite ray (light in-scattered when + // no surface hit or object behind atmosphere) + float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, startPosHeight, muStartPos, musStartPos, nuStartPos), 0.0f); + float surfacePosHeight = length(surfacePos); + float musEndPos = dot(surfacePos, atmosphericFog.AtmosphericFogSunDirection) / surfacePosHeight; + //return float3(muStartPos, 0, 0); - // check if object hit is inside atmosphere - if (pathLength < maxPathLength) - { - //return float3(1,0,0); + // check if object hit is inside atmosphere + if (pathLength < maxPathLength) + { + //return float3(1,0,0); - // reduce total in-scattered light when surface hit - // within atmosphere - // fíx described in chapter 5.1.1 - attenuation = AnalyticTransmittance(startPosHeight, muStartPos, pathLength); // TODO: optimize this!! here - float muEndPos = dot(surfacePos, viewDir) / surfacePosHeight; - float4 inscatterSurface = Texture4DSample(AtmosphereInscatterTexture, surfacePosHeight, muEndPos, musEndPos, nuStartPos); - inscatter = max(inscatter - attenuation.rgbr * inscatterSurface, 0.0f); - irradianceFactor = 1.0f; - } - else - { - //return float3(1,0,0); + // reduce total in-scattered light when surface hit + // within atmosphere + // fíx described in chapter 5.1.1 + attenuation = AnalyticTransmittance(startPosHeight, muStartPos, pathLength); // TODO: optimize this!! here + float muEndPos = dot(surfacePos, viewDir) / surfacePosHeight; + float4 inscatterSurface = Texture4DSample(AtmosphereInscatterTexture, surfacePosHeight, muEndPos, musEndPos, nuStartPos); + inscatter = max(inscatter - attenuation.rgbr * inscatterSurface, 0.0f); + irradianceFactor = 1.0f; + } + else + { + //return float3(1,0,0); - // retrieve extinction factor for inifinte ray - // fíx described in chapter 5.1.1 - attenuation = AnalyticTransmittance(startPosHeight, muStartPos, pathLength); // TODO: optimize this! and here - } - /* - // avoids imprecision problems near horizon by interpolating between - // two points above and below horizon - // fíx described in chapter 5.1.2 - float muHorizon = -sqrt(1.0 - (g_Rg / startPosHeight) * (g_Rg / startPosHeight)); - if (abs(muStartPos - muHorizon) < EPSILON_INSCATTER) - { - float mu = muHorizon - EPSILON_INSCATTER; - float samplePosHeight = sqrt(startPosHeight*startPosHeight + pathLength * pathLength + 2.0f * startPosHeight * pathLength*mu); - float muSamplePos = (startPosHeight * mu + pathLength) / samplePosHeight; - float4 inScatter0 = Texture4DSample(AtmosphereInscatterTexture, startPosHeight, mu, musStartPos, nuStartPos); - float4 inScatter1 = Texture4DSample(AtmosphereInscatterTexture, samplePosHeight, muSamplePos, musEndPos, nuStartPos); - float4 inScatterA = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0); - mu = muHorizon + EPSILON_INSCATTER; - samplePosHeight = sqrt(startPosHeight * startPosHeight + pathLength * pathLength + 2.0f * startPosHeight * pathLength * mu); - muSamplePos = (startPosHeight * mu + pathLength) / samplePosHeight; - inScatter0 = Texture4DSample(AtmosphereInscatterTexture, startPosHeight, mu, musStartPos, nuStartPos); - inScatter1 = Texture4DSample(AtmosphereInscatterTexture, samplePosHeight, muSamplePos, musEndPos, nuStartPos); - float4 inScatterB = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0f); - float t = ((muStartPos - muHorizon) + EPSILON_INSCATTER) / (2.0 * EPSILON_INSCATTER); - inscatter = lerp(inScatterA, inScatterB, t); - } - */ + // retrieve extinction factor for inifinte ray + // fíx described in chapter 5.1.1 + attenuation = AnalyticTransmittance(startPosHeight, muStartPos, pathLength); // TODO: optimize this! and here + } + /* + // avoids imprecision problems near horizon by interpolating between + // two points above and below horizon + // fíx described in chapter 5.1.2 + float muHorizon = -sqrt(1.0 - (g_Rg / startPosHeight) * (g_Rg / startPosHeight)); + if (abs(muStartPos - muHorizon) < EPSILON_INSCATTER) + { + float mu = muHorizon - EPSILON_INSCATTER; + float samplePosHeight = sqrt(startPosHeight*startPosHeight + pathLength * pathLength + 2.0f * startPosHeight * pathLength*mu); + float muSamplePos = (startPosHeight * mu + pathLength) / samplePosHeight; + float4 inScatter0 = Texture4DSample(AtmosphereInscatterTexture, startPosHeight, mu, musStartPos, nuStartPos); + float4 inScatter1 = Texture4DSample(AtmosphereInscatterTexture, samplePosHeight, muSamplePos, musEndPos, nuStartPos); + float4 inScatterA = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0); + mu = muHorizon + EPSILON_INSCATTER; + samplePosHeight = sqrt(startPosHeight * startPosHeight + pathLength * pathLength + 2.0f * startPosHeight * pathLength * mu); + muSamplePos = (startPosHeight * mu + pathLength) / samplePosHeight; + inScatter0 = Texture4DSample(AtmosphereInscatterTexture, startPosHeight, mu, musStartPos, nuStartPos); + inScatter1 = Texture4DSample(AtmosphereInscatterTexture, samplePosHeight, muSamplePos, musEndPos, nuStartPos); + float4 inScatterB = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0f); + float t = ((muStartPos - muHorizon) + EPSILON_INSCATTER) / (2.0 * EPSILON_INSCATTER); + inscatter = lerp(inScatterA, inScatterB, t); + } + */ - // avoids imprecision problems in Mie scattering when sun is below - //horizon - // fíx described in chapter 5.1.3 - inscatter.w *= smoothstep(0.00, 0.02, musStartPos); - float phaseR = PhaseFunctionR(nuStartPos); - float phaseM = PhaseFunctionM(nuStartPos); - inscatteredLight = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0f); + // avoids imprecision problems in Mie scattering when sun is below + //horizon + // fíx described in chapter 5.1.3 + inscatter.w *= smoothstep(0.00, 0.02, musStartPos); + float phaseR = PhaseFunctionR(nuStartPos); + float phaseM = PhaseFunctionM(nuStartPos); + inscatteredLight = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0f); - float sunIntensity = 10; - inscatteredLight *= sunIntensity; - } - } + float sunIntensity = 10; + inscatteredLight *= sunIntensity; + } + } - return inscatteredLight; + return inscatteredLight; } float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float3 viewPosition, float3 viewVector, float sceneDepth, float3 sceneColor) { - float4 result = float4(1, 0, 0, 1); + float4 result = float4(1, 0, 0, 1); #if 0 @@ -455,96 +454,96 @@ float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float #if 1 - // TODO: scale viewPosition from cm to km + // TODO: scale viewPosition from cm to km - float scale = 0.0001f * atmosphericFog.AtmosphericFogDistanceScale; - viewPosition.y = (viewPosition.y - atmosphericFog.AtmosphericFogGroundOffset) * atmosphericFog.AtmosphericFogAltitudeScale; - //viewPosition *= atmosphericFog.AtmosphericFogDistanceScale; - viewPosition *= scale; - //viewPosition.xz *= 0.00001f; + float scale = 0.0001f * atmosphericFog.AtmosphericFogDistanceScale; + viewPosition.y = (viewPosition.y - atmosphericFog.AtmosphericFogGroundOffset) * atmosphericFog.AtmosphericFogAltitudeScale; + //viewPosition *= atmosphericFog.AtmosphericFogDistanceScale; + viewPosition *= scale; + //viewPosition.xz *= 0.00001f; - //viewPosition *= scale; - viewPosition.y += RadiusGround + HeightOffset; + //viewPosition *= scale; + viewPosition.y += RadiusGround + HeightOffset; - //viewPosition.y -= atmosphericFog.AtmosphericFogGroundOffset; - //worldPosition - float Radius = length(viewPosition); - float3 V = normalize(viewVector); - float Mu = dot(viewPosition, V) / Radius; - float T = -Radius * Mu - sqrt(Radius * Radius * (Mu * Mu - 1.0) + RadiusGround * RadiusGround); + //viewPosition.y -= atmosphericFog.AtmosphericFogGroundOffset; + //worldPosition + float Radius = length(viewPosition); + float3 V = normalize(viewVector); + float Mu = dot(viewPosition, V) / Radius; + float T = -Radius * Mu - sqrt(Radius * Radius * (Mu * Mu - 1.0) + RadiusGround * RadiusGround); - //-Radius * Mu > sqrt(Radius * Radius * (Mu * Mu - 1.0) + RadiusGround * RadiusGround) - /* - float3 g = viewPosition - float3(0.0, 0.0, RadiusGround + 10.0); - float a = V.x * V.x + V.y * V.y - V.z * V.z; - float b = 2.0 * (g.x * V.x + g.y * V.y - g.z * V.z); - float c = g.x * g.x + g.y * g.y - g.z * g.z; - float d = -(b + sqrt(b * b - 4.0 * a * c)) / (2.0 * a); - bool cone = d > 0.0 && abs(viewPosition.z + d * V.z - RadiusGround) <= 10.0; - - if (T > 0.0) - { - if (cone && d < T) - T = d; - //return float4(1,0,0,1); - } - else if (cone) - { - T = d; - //return float4(0,0,1,1); - } - */ - //return float4(V, 1); + //-Radius * Mu > sqrt(Radius * Radius * (Mu * Mu - 1.0) + RadiusGround * RadiusGround) + /* + float3 g = viewPosition - float3(0.0, 0.0, RadiusGround + 10.0); + float a = V.x * V.x + V.y * V.y - V.z * V.z; + float b = 2.0 * (g.x * V.x + g.y * V.y - g.z * V.z); + float c = g.x * g.x + g.y * g.y - g.z * g.z; + float d = -(b + sqrt(b * b - 4.0 * a * c)) / (2.0 * a); + bool cone = d > 0.0 && abs(viewPosition.z + d * V.z - RadiusGround) <= 10.0; + + if (T > 0.0) + { + if (cone && d < T) + T = d; + //return float4(1,0,0,1); + } + else if (cone) + { + T = d; + //return float4(0,0,1,1); + } + */ + //return float4(V, 1); - // TODO: create uniform param for that depth limit - float depthThreshold = min(100.0f * atmosphericFog.AtmosphericFogDistanceScale, (viewFar * 0.997f) * scale); // 100km limit or far plane - sceneDepth *= scale; + // TODO: create uniform param for that depth limit + float depthThreshold = min(100.0f * atmosphericFog.AtmosphericFogDistanceScale, (viewFar * 0.997f) * scale); // 100km limit or far plane + sceneDepth *= scale; - float fogDepth = max(0.0f, sceneDepth - atmosphericFog.AtmosphericFogStartDistance); - float shadowFactor = 1.0f; - float DistanceRatio = min(fogDepth * 0.1f / atmosphericFog.AtmosphericFogStartDistance, 1.0f); - bool isSceneGeometry = (sceneDepth < depthThreshold); // Assume as scene geometry - //isSceneGeometry = false; - if (isSceneGeometry) - { - shadowFactor = DistanceRatio * atmosphericFog.AtmosphericFogPower; - T = max(sceneDepth + atmosphericFog.AtmosphericFogDistanceOffset, 1.0f); + float fogDepth = max(0.0f, sceneDepth - atmosphericFog.AtmosphericFogStartDistance); + float shadowFactor = 1.0f; + float DistanceRatio = min(fogDepth * 0.1f / atmosphericFog.AtmosphericFogStartDistance, 1.0f); + bool isSceneGeometry = (sceneDepth < depthThreshold); // Assume as scene geometry + //isSceneGeometry = false; + if (isSceneGeometry) + { + shadowFactor = DistanceRatio * atmosphericFog.AtmosphericFogPower; + T = max(sceneDepth + atmosphericFog.AtmosphericFogDistanceOffset, 1.0f); - // TODO: fog for scene objects - return 0; - //return float4(0, 1, 0, 1); - } + // TODO: fog for scene objects + return 0; + //return float4(0, 1, 0, 1); + } - float3 attenuation; - float3 inscatterColor = GetInscatterColor(fogDepth, viewPosition, T, V, atmosphericFog.AtmosphericFogSunDirection, Radius, Mu, attenuation, isSceneGeometry); //S[L]-T(viewPosition,xs)S[l]|xs - //float3 inscatterColor = inscatter(viewPosition, T, V, atmosphericFog.AtmosphericFogSunDirection, Radius, Mu, attenuation); //S[L]-T(viewPosition,xs)S[l]|xs - //float3 groundColor = 0; - float3 groundColor = GetGroundColor(float4(sceneColor.rgb, 1.f), viewPosition, T, V, atmosphericFog.AtmosphericFogSunDirection, Radius, attenuation, isSceneGeometry); //R[L0]+R[L*] - float3 sun = GetSunColor(atmosphericFog, viewPosition, T, V, atmosphericFog.AtmosphericFogSunDirection, Radius, Mu); //L0 + float3 attenuation; + float3 inscatterColor = GetInscatterColor(fogDepth, viewPosition, T, V, atmosphericFog.AtmosphericFogSunDirection, Radius, Mu, attenuation, isSceneGeometry); //S[L]-T(viewPosition,xs)S[l]|xs + //float3 inscatterColor = inscatter(viewPosition, T, V, atmosphericFog.AtmosphericFogSunDirection, Radius, Mu, attenuation); //S[L]-T(viewPosition,xs)S[l]|xs + //float3 groundColor = 0; + float3 groundColor = GetGroundColor(float4(sceneColor.rgb, 1.f), viewPosition, T, V, atmosphericFog.AtmosphericFogSunDirection, Radius, attenuation, isSceneGeometry); //R[L0]+R[L*] + float3 sun = GetSunColor(atmosphericFog, viewPosition, T, V, atmosphericFog.AtmosphericFogSunDirection, Radius, Mu); //L0 - float3 color = (atmosphericFog.AtmosphericFogSunPower) * (sun + groundColor + inscatterColor) * atmosphericFog.AtmosphericFogSunColor.rgb; - //return float4(color, lerp(saturate(attenuation.r * atmosphericFog.AtmosphericFogDensityScale - atmosphericFog.AtmosphericFogDensityOffset), 1.0f, (1.0f - DistanceRatio)) ); - //return lerp(saturate(attenuation.r * atmosphericFog.AtmosphericFogDensityScale - atmosphericFog.AtmosphericFogDensityOffset), 1.0f, (1.0f - DistanceRatio)); - //color *= lerp(saturate(attenuation.r * atmosphericFog.AtmosphericFogDensityScale - atmosphericFog.AtmosphericFogDensityOffset), 1.0f, (1.0f - DistanceRatio)); + float3 color = (atmosphericFog.AtmosphericFogSunPower) * (sun + groundColor + inscatterColor) * atmosphericFog.AtmosphericFogSunColor.rgb; + //return float4(color, lerp(saturate(attenuation.r * atmosphericFog.AtmosphericFogDensityScale - atmosphericFog.AtmosphericFogDensityOffset), 1.0f, (1.0f - DistanceRatio)) ); + //return lerp(saturate(attenuation.r * atmosphericFog.AtmosphericFogDensityScale - atmosphericFog.AtmosphericFogDensityOffset), 1.0f, (1.0f - DistanceRatio)); + //color *= lerp(saturate(attenuation.r * atmosphericFog.AtmosphericFogDensityScale - atmosphericFog.AtmosphericFogDensityOffset), 1.0f, (1.0f - DistanceRatio)); - //return attenuation.bbbb; + //return attenuation.bbbb; - return float4(color, 1); + return float4(color, 1); - //return float4(sun, 1); - //return float4(inscatterColor + sun, 1); - //return float4(sun + groundColor, 1); - //return float4(sun + groundColor + inscatterColor, 1); + //return float4(sun, 1); + //return float4(inscatterColor + sun, 1); + //return float4(sun + groundColor, 1); + //return float4(sun + groundColor + inscatterColor, 1); #endif - return result; + return result; } float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float3 worldPosition, float3 cameraPosition) { - float3 viewVector = worldPosition - cameraPosition; - return GetAtmosphericFog(atmosphericFog, viewFar, cameraPosition, viewVector, length(viewVector), float3(0, 0, 0)); + float3 viewVector = worldPosition - cameraPosition; + return GetAtmosphericFog(atmosphericFog, viewFar, cameraPosition, viewVector, length(viewVector), float3(0, 0, 0)); } #endif diff --git a/Source/Shaders/BRDF.hlsl b/Source/Shaders/BRDF.hlsl index 7247dd6d0..739682e3e 100644 --- a/Source/Shaders/BRDF.hlsl +++ b/Source/Shaders/BRDF.hlsl @@ -7,55 +7,55 @@ float3 Diffuse_Lambert(float3 diffuseColor) { - return diffuseColor * (1 / PI); + return diffuseColor * (1 / PI); } // GGX / Trowbridge-Reitz // [Walter et al. 2007, "Microfacet models for refraction through rough surfaces"] float D_GGX(float roughness, float NoH) { - float a = roughness * roughness; - float a2 = a * a; - float d = (NoH * a2 - NoH) * NoH + 1; - return a2 / (PI * d * d); + float a = roughness * roughness; + float a2 = a * a; + float d = (NoH * a2 - NoH) * NoH + 1; + return a2 / (PI * d * d); } // Tuned to match behavior of Vis_Smith // [Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"] float Vis_Schlick(float roughness, float NoV, float NoL) { - float k = Square(roughness) * 0.5; - float visSchlickV = NoV * (1 - k) + k; - float visSchlickL = NoL * (1 - k) + k; - return 0.25 / (visSchlickV * visSchlickL); + float k = Square(roughness) * 0.5; + float visSchlickV = NoV * (1 - k) + k; + float visSchlickL = NoL * (1 - k) + k; + return 0.25 / (visSchlickV * visSchlickL); } // Smith term for GGX // [Smith 1967, "Geometrical shadowing of a random rough surface"] float Vis_Smith(float roughness, float NoV, float NoL) { - float a = Square(roughness); - float a2 = a * a; - float visSmithV = NoV + sqrt(NoV * (NoV - NoV * a2) + a2); - float visSmithL = NoL + sqrt(NoL * (NoL - NoL * a2) + a2); - return rcp(visSmithV * visSmithL); + float a = Square(roughness); + float a2 = a * a; + float visSmithV = NoV + sqrt(NoV * (NoV - NoV * a2) + a2); + float visSmithL = NoL + sqrt(NoL * (NoL - NoL * a2) + a2); + return rcp(visSmithV * visSmithL); } // Appoximation of joint Smith term for GGX // [Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs"] float Vis_SmithJointApprox(float roughness, float NoV, float NoL) { - float a = Square(roughness); - float visSmithV = NoL * (NoV * (1 - a) + a); - float visSmithL = NoV * (NoL * (1 - a) + a); - return 0.5 * rcp(visSmithV + visSmithL); + float a = Square(roughness); + float visSmithV = NoL * (NoV * (1 - a) + a); + float visSmithL = NoV * (NoL * (1 - a) + a); + return 0.5 * rcp(visSmithV + visSmithL); } // [Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"] float3 F_Schlick(float3 specularColor, float VoH) { - float fc = Pow5(1 - VoH); - return saturate(50.0 * specularColor.g) * fc + (1 - fc) * specularColor; + float fc = Pow5(1 - VoH); + return saturate(50.0 * specularColor.g) * fc + (1 - fc) * specularColor; } #define REFLECTION_CAPTURE_NUM_MIPS 7 @@ -64,44 +64,44 @@ float3 F_Schlick(float3 specularColor, float VoH) half ProbeMipFromRoughness(half roughness) { - half mip1px = REFLECTION_CAPTURE_ROUGHEST_MIP - REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE * log2(roughness); - return REFLECTION_CAPTURE_NUM_MIPS - 1 - mip1px; + half mip1px = REFLECTION_CAPTURE_ROUGHEST_MIP - REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE * log2(roughness); + return REFLECTION_CAPTURE_NUM_MIPS - 1 - mip1px; } half SSRMipFromRoughness(half roughness) { - half mip1px = 4 - REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE * log2(roughness); - return max(1, 10 - mip1px); + half mip1px = 4 - REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE * log2(roughness); + return max(1, 10 - mip1px); } float ProbeRoughnessFromMip(float mip) { - float mip1px = REFLECTION_CAPTURE_NUM_MIPS - 1 - mip; - return exp2((REFLECTION_CAPTURE_ROUGHEST_MIP - mip1px) / REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE); + float mip1px = REFLECTION_CAPTURE_NUM_MIPS - 1 - mip; + return exp2((REFLECTION_CAPTURE_ROUGHEST_MIP - mip1px) / REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE); } // [Lazarov 2013, "Getting More Physical in Call of Duty: Black Ops II"] float3 EnvBRDFApprox(float3 specularColor, float roughness, float NoV) { - // Approximate version, base for pre integrated version - const half4 c0 = { -1, -0.0275, -0.572, 0.022 }; - const half4 c1 = { 1, 0.0425, 1.04, -0.04 }; - half4 r = roughness * c0 + c1; - half a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y; - half2 ab = half2(-1.04, 1.04) * a004 + r.zw; - return specularColor * ab.x + saturate(50.0 * specularColor.g) * ab.y; + // Approximate version, base for pre integrated version + const half4 c0 = { -1, -0.0275, -0.572, 0.022 }; + const half4 c1 = { 1, 0.0425, 1.04, -0.04 }; + half4 r = roughness * c0 + c1; + half a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y; + half2 ab = half2(-1.04, 1.04) * a004 + r.zw; + return specularColor * ab.x + saturate(50.0 * specularColor.g) * ab.y; } // Importance sampled preintegrated G * F float3 EnvBRDF(Texture2D preIntegratedGF, float3 specularColor, float roughness, float NoV) { - float2 ab = preIntegratedGF.SampleLevel(SamplerLinearClamp, float2(NoV, roughness), 0).rg; - return specularColor * ab.x + saturate(50.0 * specularColor.g) * ab.y; + float2 ab = preIntegratedGF.SampleLevel(SamplerLinearClamp, float2(NoV, roughness), 0).rg; + return specularColor * ab.x + saturate(50.0 * specularColor.g) * ab.y; } float RoughnessToSpecularPower(float roughness) { - return pow(2, 13 * (1 - roughness)); + return pow(2, 13 * (1 - roughness)); } #endif diff --git a/Source/Shaders/Collisions.hlsl b/Source/Shaders/Collisions.hlsl index 4a62aebc4..6794bc6fe 100644 --- a/Source/Shaders/Collisions.hlsl +++ b/Source/Shaders/Collisions.hlsl @@ -5,17 +5,17 @@ bool RayHitSphere(float3 r, float3 sphereCenter, float sphereRadius) { - float3 closestPointOnRay = max(0, dot(sphereCenter, r)) * r; - float3 centerToRay = closestPointOnRay - sphereCenter; - return dot(centerToRay, centerToRay) <= (sphereRadius * sphereRadius); + float3 closestPointOnRay = max(0, dot(sphereCenter, r)) * r; + float3 centerToRay = closestPointOnRay - sphereCenter; + return dot(centerToRay, centerToRay) <= (sphereRadius * sphereRadius); } bool RayHitRect(float3 r, float3 rectCenter, float3 rectX, float3 rectY, float3 rectZ, float rectExtentX, float rectExtentY) { - float3 pointOnPlane = r * max(0, dot(rectZ, rectCenter) / dot(rectZ, r)); - bool inExtentX = abs(dot(rectX, pointOnPlane - rectCenter)) <= rectExtentX; - bool inExtentY = abs(dot(rectY, pointOnPlane - rectCenter)) <= rectExtentY; - return inExtentX && inExtentY; + float3 pointOnPlane = r * max(0, dot(rectZ, rectCenter) / dot(rectZ, r)); + bool inExtentX = abs(dot(rectX, pointOnPlane - rectCenter)) <= rectExtentX; + bool inExtentY = abs(dot(rectY, pointOnPlane - rectCenter)) <= rectExtentY; + return inExtentX && inExtentY; } // Hits axis-aligned box (boxMin, boxMax) with a line (lineStart, lineEnd). @@ -24,21 +24,21 @@ bool RayHitRect(float3 r, float3 rectCenter, float3 rectX, float3 rectY, float3 // Hit point is: hitPoint = lineStart + (lineEnd - lineStart) * intersections.x/y. float2 LineHitBox(float3 lineStart, float3 lineEnd, float3 boxMin, float3 boxMax) { - float3 invDirection = 1.0f / (lineEnd - lineStart); - float3 enterIntersection = (boxMin - lineStart) * invDirection; - float3 exitIntersection = (boxMax - lineStart) * invDirection; - float3 minIntersections = min(enterIntersection, exitIntersection); - float3 maxIntersections = max(enterIntersection, exitIntersection); - float2 intersections; - intersections.x = max(minIntersections.x, max(minIntersections.y, minIntersections.z)); - intersections.y = min(maxIntersections.x, min(maxIntersections.y, maxIntersections.z)); - return saturate(intersections); + float3 invDirection = 1.0f / (lineEnd - lineStart); + float3 enterIntersection = (boxMin - lineStart) * invDirection; + float3 exitIntersection = (boxMax - lineStart) * invDirection; + float3 minIntersections = min(enterIntersection, exitIntersection); + float3 maxIntersections = max(enterIntersection, exitIntersection); + float2 intersections; + intersections.x = max(minIntersections.x, max(minIntersections.y, minIntersections.z)); + intersections.y = min(maxIntersections.x, min(maxIntersections.y, maxIntersections.z)); + return saturate(intersections); } // Determines whether there is an intersection between a box and a sphere. bool BoxIntersectsSphere(float3 boxMin, float3 boxMax, float3 sphereCenter, float sphereRadius) { - const float3 clampedCenter = clamp(sphereCenter, boxMin, boxMax); + const float3 clampedCenter = clamp(sphereCenter, boxMin, boxMax); return distance(sphereCenter, clampedCenter) <= sphereRadius; } diff --git a/Source/Shaders/Common.hlsl b/Source/Shaders/Common.hlsl index fd5fc19f4..1bc9e718c 100644 --- a/Source/Shaders/Common.hlsl +++ b/Source/Shaders/Common.hlsl @@ -134,12 +134,12 @@ SamplerComparisonState ShadowSamplerPCF : register(s5); // Structure that contains information about GBuffer struct GBufferData { - float4 ViewInfo; // x-1/Projection[0,0] y-1/Projection[1,1] z-(Far / (Far - Near) w-(-Far * Near) / (Far - Near) / Far) - float4 ScreenSize; // x-Width y-Height z-1/Width w-1/Height - float3 ViewPos; // view position (in world space) - float ViewFar; // view far plane distance (in world space) - float4x4 InvViewMatrix; // inverse view matrix (4 rows by 4 columns) - float4x4 InvProjectionMatrix; // inverse projection matrix (4 rows by 4 columns) + float4 ViewInfo; // x-1/Projection[0,0], y-1/Projection[1,1], z-(Far / (Far - Near), w-(-Far * Near) / (Far - Near) / Far) + float4 ScreenSize; // x-Width, y-Height, z-1/Width, w-1/Height + float3 ViewPos; // view position (in world space) + float ViewFar; // view far plane distance (in world space) + float4x4 InvViewMatrix; // inverse view matrix (4 rows by 4 columns) + float4x4 InvProjectionMatrix; // inverse projection matrix (4 rows by 4 columns) }; #ifdef PLATFORM_ANDROID @@ -152,28 +152,28 @@ struct GBufferData // Structure that contains information about atmosphere fog struct AtmosphericFogData { - float AtmosphericFogDensityScale; - float AtmosphericFogSunDiscScale; - float AtmosphericFogDistanceScale; - float AtmosphericFogGroundOffset; + float AtmosphericFogDensityScale; + float AtmosphericFogSunDiscScale; + float AtmosphericFogDistanceScale; + float AtmosphericFogGroundOffset; - float AtmosphericFogAltitudeScale; - float AtmosphericFogStartDistance; - float AtmosphericFogPower; - float AtmosphericFogDistanceOffset; + float AtmosphericFogAltitudeScale; + float AtmosphericFogStartDistance; + float AtmosphericFogPower; + float AtmosphericFogDistanceOffset; - float3 AtmosphericFogSunDirection; - float AtmosphericFogSunPower; + float3 AtmosphericFogSunDirection; + float AtmosphericFogSunPower; - float3 AtmosphericFogSunColor; - float AtmosphericFogDensityOffset; + float3 AtmosphericFogSunColor; + float AtmosphericFogDensityOffset; }; // Packed env probe data struct ProbeData { - float4 Data0; // x - Position.x, y - Position.y, z - Position.z, w - unused - float4 Data1; // x - Radius , y - 1 / Radius, z - Brightness, w - unused + float4 Data0; // x - Position.x, y - Position.y, z - Position.z, w - unused + float4 Data1; // x - Radius , y - 1 / Radius, z - Brightness, w - unused }; #define ProbePos Data0.xyz @@ -183,25 +183,25 @@ struct ProbeData struct Quad_VS2PS { - float4 Position : SV_Position; - noperspective float2 TexCoord : TEXCOORD0; + float4 Position : SV_Position; + noperspective float2 TexCoord : TEXCOORD0; }; struct Quad_VS2GS { - Quad_VS2PS Vertex; - uint LayerIndex : TEXCOORD1; + Quad_VS2PS Vertex; + uint LayerIndex : TEXCOORD1; }; struct Quad_GS2PS { - Quad_VS2PS Vertex; - uint LayerIndex : SV_RenderTargetArrayIndex; + Quad_VS2PS Vertex; + uint LayerIndex : SV_RenderTargetArrayIndex; }; float Luminance(float3 color) { - return dot(color, float3(0.299f, 0.587f, 0.114f)); + return dot(color, float3(0.299f, 0.587f, 0.114f)); } // Quaternion multiplication (http://mathworld.wolfram.com/Quaternion.html) @@ -220,13 +220,13 @@ float3 QuatRotateVector(float4 q, float3 v) // Samples the unwrapped 3D texture (eg. volume texture of size 16x16x16 would be unwrapped to 256x16) float4 SampleUnwrappedTexture3D(Texture2D tex, SamplerState s, float3 uvw, float size) { - float intW = floor(uvw.z * size - 0.5); - half fracW = uvw.z * size - 0.5 - intW; - float u = (uvw.x + intW) / size; - float v = uvw.y; - float4 rg0 = tex.Sample(s, float2(u, v)); - float4 rg1 = tex.Sample(s, float2(u + 1.0f / size, v)); - return lerp(rg0, rg1, fracW); + float intW = floor(uvw.z * size - 0.5); + half fracW = uvw.z * size - 0.5 - intW; + float u = (uvw.x + intW) / size; + float v = uvw.y; + float4 rg0 = tex.Sample(s, float2(u, v)); + float4 rg1 = tex.Sample(s, float2(u + 1.0f / size, v)); + return lerp(rg0, rg1, fracW); } #endif diff --git a/Source/Shaders/Editor/QuadOverdraw.hlsl b/Source/Shaders/Editor/QuadOverdraw.hlsl index 12e9ba9a5..acb411108 100644 --- a/Source/Shaders/Editor/QuadOverdraw.hlsl +++ b/Source/Shaders/Editor/QuadOverdraw.hlsl @@ -3,46 +3,46 @@ #define __QUAD_OVERDRAW__ RWTexture2D lockUAV : register(u0); -RWTexture2D overdrawUAV : register(u1); +RWTexture2D overdrawUAV : register(u1); RWTexture2D liveCountUAV : register(u2); void DoQuadOverdraw(float4 svPos, uint primId) { - uint2 quad = svPos.xy * 0.5; - uint prevID; - uint unlockedID = 0xffffffff; - bool processed = false; - int lockCount = 0; - int pixelCount = 0; + uint2 quad = svPos.xy * 0.5; + uint prevID; + uint unlockedID = 0xffffffff; + bool processed = false; + int lockCount = 0; + int pixelCount = 0; - for (int i = 0; i < 64; i++) - { - if (!processed) - InterlockedCompareExchange(lockUAV[quad], unlockedID, primId, prevID); - [branch] - if (prevID == unlockedID) - { - if (++lockCount == 4) - { - // Retrieve live pixel count (minus 1) in quad - InterlockedAnd(liveCountUAV[quad], 0, pixelCount); + for (int i = 0; i < 64; i++) + { + if (!processed) + InterlockedCompareExchange(lockUAV[quad], unlockedID, primId, prevID); + [branch] + if (prevID == unlockedID) + { + if (++lockCount == 4) + { + // Retrieve live pixel count (minus 1) in quad + InterlockedAnd(liveCountUAV[quad], 0, pixelCount); - // Unlock for other quads - InterlockedExchange(lockUAV[quad], unlockedID, prevID); - } - processed = true; - } - if (prevID == primId && !processed) - { - InterlockedAdd(liveCountUAV[quad], 1); - processed = true; - } - } + // Unlock for other quads + InterlockedExchange(lockUAV[quad], unlockedID, prevID); + } + processed = true; + } + if (prevID == primId && !processed) + { + InterlockedAdd(liveCountUAV[quad], 1); + processed = true; + } + } - if (lockCount) - { - InterlockedAdd(overdrawUAV[quad], 1); - } + if (lockCount) + { + InterlockedAdd(overdrawUAV[quad], 1); + } } #endif diff --git a/Source/Shaders/ExponentialHeightFog.hlsl b/Source/Shaders/ExponentialHeightFog.hlsl index d3008857e..d94ef5e0c 100644 --- a/Source/Shaders/ExponentialHeightFog.hlsl +++ b/Source/Shaders/ExponentialHeightFog.hlsl @@ -9,82 +9,82 @@ // Structure that contains information about exponential height fog struct ExponentialHeightFogData { - float3 FogInscatteringColor; - float FogMinOpacity; + float3 FogInscatteringColor; + float FogMinOpacity; - float FogDensity; - float FogHeight; - float FogHeightFalloff; - float FogAtViewPosition; + float FogDensity; + float FogHeight; + float FogHeightFalloff; + float FogAtViewPosition; - float3 InscatteringLightDirection; - float ApplyDirectionalInscattering; + float3 InscatteringLightDirection; + float ApplyDirectionalInscattering; - float3 DirectionalInscatteringColor; - float DirectionalInscatteringExponent; + float3 DirectionalInscatteringColor; + float DirectionalInscatteringExponent; - float FogCutoffDistance; - float VolumetricFogMaxDistance; - float DirectionalInscatteringStartDistance; - float StartDistance; + float FogCutoffDistance; + float VolumetricFogMaxDistance; + float DirectionalInscatteringStartDistance; + float StartDistance; }; float4 GetExponentialHeightFog(ExponentialHeightFogData exponentialHeightFog, float3 posWS, float3 camWS, float skipDistance) { - float3 cameraToPos = posWS - camWS; - float cameraToPosSqr = dot(cameraToPos, cameraToPos); - float cameraToPosLenInv = rsqrt(cameraToPosSqr); - float cameraToPosLen = cameraToPosSqr * cameraToPosLenInv; - float3 cameraToReceiverNorm = cameraToPos * cameraToPosLenInv; + float3 cameraToPos = posWS - camWS; + float cameraToPosSqr = dot(cameraToPos, cameraToPos); + float cameraToPosLenInv = rsqrt(cameraToPosSqr); + float cameraToPosLen = cameraToPosSqr * cameraToPosLenInv; + float3 cameraToReceiverNorm = cameraToPos * cameraToPosLenInv; - float rayOriginTerms = exponentialHeightFog.FogAtViewPosition; - float rayLength = cameraToPosLen; - float rayDirectionY = cameraToPos.y; + float rayOriginTerms = exponentialHeightFog.FogAtViewPosition; + float rayLength = cameraToPosLen; + float rayDirectionY = cameraToPos.y; - // Apply start distance offset - skipDistance = max(skipDistance, exponentialHeightFog.StartDistance); - if (skipDistance > 0) - { - float excludeIntersectionTime = skipDistance * cameraToPosLenInv; - float cameraToExclusionIntersectionY = excludeIntersectionTime * cameraToPos.y; - float exclusionIntersectionY = camWS.y + cameraToExclusionIntersectionY; - rayLength = (1.0f - excludeIntersectionTime) * cameraToPosLen; - rayDirectionY = cameraToPos.y - cameraToExclusionIntersectionY; - float exponent = exponentialHeightFog.FogHeightFalloff * (exclusionIntersectionY - exponentialHeightFog.FogHeight); - rayOriginTerms = exponentialHeightFog.FogDensity * exp2(-exponent); - } + // Apply start distance offset + skipDistance = max(skipDistance, exponentialHeightFog.StartDistance); + if (skipDistance > 0) + { + float excludeIntersectionTime = skipDistance * cameraToPosLenInv; + float cameraToExclusionIntersectionY = excludeIntersectionTime * cameraToPos.y; + float exclusionIntersectionY = camWS.y + cameraToExclusionIntersectionY; + rayLength = (1.0f - excludeIntersectionTime) * cameraToPosLen; + rayDirectionY = cameraToPos.y - cameraToExclusionIntersectionY; + float exponent = exponentialHeightFog.FogHeightFalloff * (exclusionIntersectionY - exponentialHeightFog.FogHeight); + rayOriginTerms = exponentialHeightFog.FogDensity * exp2(-exponent); + } - // Calculate the integral of the ray starting from the view to the object position with the fog density function - float falloff = max(-127.0f, exponentialHeightFog.FogHeightFalloff * rayDirectionY); - float lineIntegral = (1.0f - exp2(-falloff)) / falloff; - float lineIntegralTaylor = log(2.0f) - (0.5f * Pow2(log(2.0f))) * falloff; - float exponentialHeightLineIntegralCalc = rayOriginTerms * (abs(falloff) > 0.01f ? lineIntegral : lineIntegralTaylor); - float exponentialHeightLineIntegral = exponentialHeightLineIntegralCalc * rayLength; + // Calculate the integral of the ray starting from the view to the object position with the fog density function + float falloff = max(-127.0f, exponentialHeightFog.FogHeightFalloff * rayDirectionY); + float lineIntegral = (1.0f - exp2(-falloff)) / falloff; + float lineIntegralTaylor = log(2.0f) - (0.5f * Pow2(log(2.0f))) * falloff; + float exponentialHeightLineIntegralCalc = rayOriginTerms * (abs(falloff) > 0.01f ? lineIntegral : lineIntegralTaylor); + float exponentialHeightLineIntegral = exponentialHeightLineIntegralCalc * rayLength; - // Calculate the light that went through the fog - float expFogFactor = max(saturate(exp2(-exponentialHeightLineIntegral)), exponentialHeightFog.FogMinOpacity); + // Calculate the light that went through the fog + float expFogFactor = max(saturate(exp2(-exponentialHeightLineIntegral)), exponentialHeightFog.FogMinOpacity); - // Calculate the directional light inscattering - float3 inscatteringColor = exponentialHeightFog.FogInscatteringColor; - float3 directionalInscattering = 0; - BRANCH - if (exponentialHeightFog.ApplyDirectionalInscattering > 0) - { - float3 directionalLightInscattering = exponentialHeightFog.DirectionalInscatteringColor * pow(saturate(dot(cameraToReceiverNorm, exponentialHeightFog.InscatteringLightDirection)), exponentialHeightFog.DirectionalInscatteringExponent); - float dirExponentialHeightLineIntegral = exponentialHeightLineIntegralCalc * max(rayLength - exponentialHeightFog.DirectionalInscatteringStartDistance, 0.0f); - float directionalInscatteringFogFactor = saturate(exp2(-dirExponentialHeightLineIntegral)); - directionalInscattering = directionalLightInscattering * (1.0f - directionalInscatteringFogFactor); - } + // Calculate the directional light inscattering + float3 inscatteringColor = exponentialHeightFog.FogInscatteringColor; + float3 directionalInscattering = 0; + BRANCH + if (exponentialHeightFog.ApplyDirectionalInscattering > 0) + { + float3 directionalLightInscattering = exponentialHeightFog.DirectionalInscatteringColor * pow(saturate(dot(cameraToReceiverNorm, exponentialHeightFog.InscatteringLightDirection)), exponentialHeightFog.DirectionalInscatteringExponent); + float dirExponentialHeightLineIntegral = exponentialHeightLineIntegralCalc * max(rayLength - exponentialHeightFog.DirectionalInscatteringStartDistance, 0.0f); + float directionalInscatteringFogFactor = saturate(exp2(-dirExponentialHeightLineIntegral)); + directionalInscattering = directionalLightInscattering * (1.0f - directionalInscatteringFogFactor); + } - // Disable fog after a certain distance - FLATTEN - if (exponentialHeightFog.FogCutoffDistance > 0 && cameraToPosLen > exponentialHeightFog.FogCutoffDistance) - { - expFogFactor = 1; - directionalInscattering = 0; - } + // Disable fog after a certain distance + FLATTEN + if (exponentialHeightFog.FogCutoffDistance > 0 && cameraToPosLen > exponentialHeightFog.FogCutoffDistance) + { + expFogFactor = 1; + directionalInscattering = 0; + } - return float4(inscatteringColor * (1.0f - expFogFactor) + directionalInscattering, expFogFactor); + return float4(inscatteringColor * (1.0f - expFogFactor) + directionalInscattering, expFogFactor); } #endif diff --git a/Source/Shaders/GBuffer.hlsl b/Source/Shaders/GBuffer.hlsl index 9ec390135..9288ab8a6 100644 --- a/Source/Shaders/GBuffer.hlsl +++ b/Source/Shaders/GBuffer.hlsl @@ -11,7 +11,7 @@ Texture2D GBuffer0 : register(t0); Texture2D GBuffer1 : register(t1); Texture2D GBuffer2 : register(t2); -Texture2D Depth : register(t3); +Texture2D Depth : register(t3); #if defined(USE_GBUFFER_CUSTOM_DATA) Texture2D GBuffer3 : register(t4); #endif @@ -27,28 +27,28 @@ Texture2D GBuffer3 : register(t4); // Linearize raw device depth float LinearizeZ(GBufferData gBuffer, float depth) { - return gBuffer.ViewInfo.w / (depth - gBuffer.ViewInfo.z); + return gBuffer.ViewInfo.w / (depth - gBuffer.ViewInfo.z); } // Convert linear depth to device depth float LinearZ2DeviceDepth(GBufferData gBuffer, float linearDepth) { - return (gBuffer.ViewInfo.w / linearDepth) + gBuffer.ViewInfo.z; + return (gBuffer.ViewInfo.w / linearDepth) + gBuffer.ViewInfo.z; } // Get view space position at given pixel coordinate with given device depth float3 GetViewPos(GBufferData gBuffer, float2 uv, float deviceDepth) { - float4 clipPos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), deviceDepth, 1.0); - float4 viewPos = mul(clipPos, gBuffer.InvProjectionMatrix); - return viewPos.xyz / viewPos.w; + float4 clipPos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), deviceDepth, 1.0); + float4 viewPos = mul(clipPos, gBuffer.InvProjectionMatrix); + return viewPos.xyz / viewPos.w; } // Get world space position at given pixel coordinate with given device depth float3 GetWorldPos(GBufferData gBuffer, float2 uv, float deviceDepth) { - float3 viewPos = GetViewPos(gBuffer, uv, deviceDepth); - return mul(float4(viewPos, 1), gBuffer.InvViewMatrix).xyz; + float3 viewPos = GetViewPos(gBuffer, uv, deviceDepth); + return mul(float4(viewPos, 1), gBuffer.InvViewMatrix).xyz; } #if !defined(NO_GBUFFER_SAMPLING) @@ -56,92 +56,92 @@ float3 GetWorldPos(GBufferData gBuffer, float2 uv, float deviceDepth) // Sample raw device depth buffer float SampleZ(float2 uv) { - return SAMPLE_RT(Depth, uv).r; + return SAMPLE_RT(Depth, uv).r; } // Sample linear depth float SampleDepth(GBufferData gBuffer, float2 uv) { - float deviceDepth = SampleZ(uv); - return LinearizeZ(gBuffer, deviceDepth); + float deviceDepth = SampleZ(uv); + return LinearizeZ(gBuffer, deviceDepth); } // Get view space position at given pixel coordinate float3 GetViewPos(GBufferData gBuffer, float2 uv) { - float deviceDepth = SampleZ(uv); - return GetViewPos(gBuffer, uv, deviceDepth); + float deviceDepth = SampleZ(uv); + return GetViewPos(gBuffer, uv, deviceDepth); } // Get world space position at given pixel coordinate float3 GetWorldPos(GBufferData gBuffer, float2 uv) { - float deviceDepth = SampleZ(uv); - return GetWorldPos(gBuffer, uv, deviceDepth); + float deviceDepth = SampleZ(uv); + return GetWorldPos(gBuffer, uv, deviceDepth); } // Sample normal vector with pixel shading model float3 SampleNormal(float2 uv, out int shadingModel) { - // Sample GBuffer - float4 gBuffer1 = SAMPLE_RT(GBuffer1, uv); + // Sample GBuffer + float4 gBuffer1 = SAMPLE_RT(GBuffer1, uv); - // Decode normal and shading model - shadingModel = (int)(gBuffer1.a * 3.999); - return DecodeNormal(gBuffer1.rgb); + // Decode normal and shading model + shadingModel = (int)(gBuffer1.a * 3.999); + return DecodeNormal(gBuffer1.rgb); } // Sample GBuffer GBufferSample SampleGBuffer(GBufferData gBuffer, float2 uv) { - GBufferSample result; + GBufferSample result; - // Sample GBuffer - float4 gBuffer0 = SAMPLE_RT(GBuffer0, uv); - float4 gBuffer1 = SAMPLE_RT(GBuffer1, uv); - float4 gBuffer2 = SAMPLE_RT(GBuffer2, uv); + // Sample GBuffer + float4 gBuffer0 = SAMPLE_RT(GBuffer0, uv); + float4 gBuffer1 = SAMPLE_RT(GBuffer1, uv); + float4 gBuffer2 = SAMPLE_RT(GBuffer2, uv); #if defined(USE_GBUFFER_CUSTOM_DATA) float4 gBuffer3 = SAMPLE_RT(GBuffer3, uv); #endif - // Decode normal and shading model - result.Normal = DecodeNormal(gBuffer1.rgb); - result.ShadingModel = (int)(gBuffer1.a * 3.999); + // Decode normal and shading model + result.Normal = DecodeNormal(gBuffer1.rgb); + result.ShadingModel = (int)(gBuffer1.a * 3.999); - // Calculate view space and world space positions - result.ViewPos = GetViewPos(gBuffer, uv); - result.WorldPos = mul(float4(result.ViewPos, 1), gBuffer.InvViewMatrix).xyz; + // Calculate view space and world space positions + result.ViewPos = GetViewPos(gBuffer, uv); + result.WorldPos = mul(float4(result.ViewPos, 1), gBuffer.InvViewMatrix).xyz; - // Decode GBuffer data - result.Color = gBuffer0.rgb; - result.AO = gBuffer0.a; - result.Roughness = gBuffer2.r; - result.Metalness = gBuffer2.g; - result.Specular = gBuffer2.b; + // Decode GBuffer data + result.Color = gBuffer0.rgb; + result.AO = gBuffer0.a; + result.Roughness = gBuffer2.r; + result.Metalness = gBuffer2.g; + result.Specular = gBuffer2.b; #if defined(USE_GBUFFER_CUSTOM_DATA) result.CustomData = gBuffer3; #endif - return result; + return result; } // Sample GBuffer (fast - only few parameters are being sampled) GBufferSample SampleGBufferFast(GBufferData gBuffer, float2 uv) { - GBufferSample result; + GBufferSample result; - // Sample GBuffer - float4 gBuffer1 = SAMPLE_RT(GBuffer1, uv); + // Sample GBuffer + float4 gBuffer1 = SAMPLE_RT(GBuffer1, uv); - // Decode normal and shading model - result.Normal = DecodeNormal(gBuffer1.rgb); - result.ShadingModel = (int)(gBuffer1.a * 3.999); + // Decode normal and shading model + result.Normal = DecodeNormal(gBuffer1.rgb); + result.ShadingModel = (int)(gBuffer1.a * 3.999); - // Calculate view space position - result.ViewPos = GetViewPos(gBuffer, uv); - result.WorldPos = mul(float4(result.ViewPos, 1), gBuffer.InvViewMatrix).xyz; + // Calculate view space position + result.ViewPos = GetViewPos(gBuffer, uv); + result.WorldPos = mul(float4(result.ViewPos, 1), gBuffer.InvViewMatrix).xyz; - return result; + return result; } #if defined(USE_GBUFFER_CUSTOM_DATA) diff --git a/Source/Shaders/GBufferCommon.hlsl b/Source/Shaders/GBufferCommon.hlsl index e78fec549..ebeeaefde 100644 --- a/Source/Shaders/GBufferCommon.hlsl +++ b/Source/Shaders/GBufferCommon.hlsl @@ -8,15 +8,15 @@ // GBuffer sample data structure struct GBufferSample { - float3 Normal; - float Roughness; - float3 WorldPos; - float Metalness; - float3 Color; - float Specular; - float3 ViewPos; - float AO; - int ShadingModel; + float3 Normal; + float Roughness; + float3 WorldPos; + float Metalness; + float3 Color; + float Specular; + float3 ViewPos; + float AO; + int ShadingModel; #if defined(USE_GBUFFER_CUSTOM_DATA) float4 CustomData; #endif @@ -24,45 +24,45 @@ struct GBufferSample bool IsSubsurfaceMode(int shadingModel) { - return shadingModel == SHADING_MODEL_SUBSURFACE || shadingModel == SHADING_MODEL_FOLIAGE; + return shadingModel == SHADING_MODEL_SUBSURFACE || shadingModel == SHADING_MODEL_FOLIAGE; } float3 GetDiffuseColor(in float3 color, in float metalness) { - return color - color * metalness; + return color - color * metalness; } float3 GetSpecularColor(in float3 color, in float specular, in float metalness) { - return lerp(0.08 * specular.xxx, color.rgb, metalness.xxx); + return lerp(0.08 * specular.xxx, color.rgb, metalness.xxx); } // Calculate material diffuse color float3 GetDiffuseColor(in GBufferSample gBuffer) { - return gBuffer.Color - gBuffer.Color * gBuffer.Metalness; + return gBuffer.Color - gBuffer.Color * gBuffer.Metalness; } // Calculate material specular color float3 GetSpecularColor(in GBufferSample gBuffer) { - return lerp(0.08 * gBuffer.Specular.xxx, gBuffer.Color.rgb, gBuffer.Metalness.xxx); + return lerp(0.08 * gBuffer.Specular.xxx, gBuffer.Color.rgb, gBuffer.Metalness.xxx); } // Compact Normal Storage for Small G-Buffers // [Aras Pranckevièius 2010, http://aras-p.info/texts/CompactNormalStorage.html] float3 EncodeNormal(float3 n) { - // Pack to [0;1] range - return n * 0.5 + 0.5; + // Pack to [0;1] range + return n * 0.5 + 0.5; } // Compact Normal Storage for Small G-Buffers // [Aras Pranckevièius 2010, http://aras-p.info/texts/CompactNormalStorage.html] float3 DecodeNormal(float3 enc) { - // Unpack from [0;1] range - return normalize(enc * 2 - 1); + // Unpack from [0;1] range + return normalize(enc * 2 - 1); } #endif diff --git a/Source/Shaders/GI/DDGI.hlsl b/Source/Shaders/GI/DDGI.hlsl index 2452cccdf..78a11bb2c 100644 --- a/Source/Shaders/GI/DDGI.hlsl +++ b/Source/Shaders/GI/DDGI.hlsl @@ -13,8 +13,9 @@ #include "./Flax/Math.hlsl" #include "./Flax/Octahedral.hlsl" -#define DDGI_PROBE_STATE_ACTIVE 0 -#define DDGI_PROBE_STATE_INACTIVE 1 +#define DDGI_PROBE_STATE_INACTIVE 0.0f +#define DDGI_PROBE_STATE_ACTIVATED 0.2f +#define DDGI_PROBE_STATE_ACTIVE 1.0f #define DDGI_PROBE_RESOLUTION_IRRADIANCE 6 // Resolution (in texels) for probe irradiance data (excluding 1px padding on each side) #define DDGI_PROBE_RESOLUTION_DISTANCE 14 // Resolution (in texels) for probe distance data (excluding 1px padding on each side) #define DDGI_SRGB_BLENDING 1 // Enables blending in sRGB color space, otherwise irradiance blending is done in linear space @@ -23,8 +24,7 @@ struct DDGIData { float4 ProbesOriginAndSpacing[4]; - int4 ProbesScrollOffsets[4]; - int4 ProbeScrollDirections[4]; + int4 ProbesScrollOffsets[4]; // w unused uint3 ProbesCounts; uint CascadesCount; float IrradianceGamma; @@ -32,7 +32,7 @@ struct DDGIData float RayMaxDistance; float IndirectLightingIntensity; float4 RaysRotation; - float3 ViewDir; + float3 ViewPos; uint RaysCount; float3 FallbackIrradiance; float Padding0; @@ -77,7 +77,7 @@ uint2 GetDDGIProbeTexelCoords(DDGIData data, uint cascadeIndex, uint probeIndex) uint GetDDGIScrollingProbeIndex(DDGIData data, uint cascadeIndex, uint3 probeCoords) { // Probes are scrolled on edges to stabilize GI when camera moves - return GetDDGIProbeIndex(data, (probeCoords + data.ProbesScrollOffsets[cascadeIndex].xyz + data.ProbesCounts) % data.ProbesCounts); + return GetDDGIProbeIndex(data, ((int3)probeCoords + data.ProbesScrollOffsets[cascadeIndex].xyz + (int3)data.ProbesCounts) % (int3)data.ProbesCounts); } float3 GetDDGIProbeWorldPosition(DDGIData data, uint cascadeIndex, uint3 probeCoords) @@ -90,7 +90,7 @@ float3 GetDDGIProbeWorldPosition(DDGIData data, uint cascadeIndex, uint3 probeCo } // Loads probe probe state -float LoadDDGIProbeState(DDGIData data, Texture2D probesState, uint cascadeIndex, uint probeIndex) +float LoadDDGIProbeState(DDGIData data, Texture2D probesState, uint cascadeIndex, uint probeIndex) { int2 probeDataCoords = GetDDGIProbeTexelCoords(data, cascadeIndex, probeIndex); float4 probeState = probesState.Load(int3(probeDataCoords, 0)); @@ -98,11 +98,12 @@ float LoadDDGIProbeState(DDGIData data, Texture2D probesState, uint casc } // Loads probe world-space position (XYZ) and probe state (W) -float4 LoadDDGIProbePositionAndState(DDGIData data, Texture2D probesState, uint cascadeIndex, uint probeIndex, uint3 probeCoords) +float4 LoadDDGIProbePositionAndState(DDGIData data, Texture2D probesState, uint cascadeIndex, uint probeIndex, uint3 probeCoords) { int2 probeDataCoords = GetDDGIProbeTexelCoords(data, cascadeIndex, probeIndex); float4 probeState = probesState.Load(int3(probeDataCoords, 0)); - probeState.xyz += GetDDGIProbeWorldPosition(data, cascadeIndex, probeCoords); + probeState.xyz *= data.ProbesOriginAndSpacing[cascadeIndex].w; // Probe offset is [-1;1] within probes spacing + probeState.xyz += GetDDGIProbeWorldPosition(data, cascadeIndex, probeCoords); // Place probe on a grid return probeState; } @@ -119,8 +120,9 @@ float2 GetDDGIProbeUV(DDGIData data, uint cascadeIndex, uint probeIndex, float2 } // Samples DDGI probes volume at the given world-space position and returns the irradiance. -// rand - randomized per-pixel value in range 0-1, used to smooth dithering for cascades blending -float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesState, Texture2D probesDistance, Texture2D probesIrradiance, float3 worldPosition, float3 worldNormal, float bias, float dither = 0.0f) +// bias - scales the bias vector to the initial sample point to reduce self-shading artifacts +// dither - randomized per-pixel value in range 0-1, used to smooth dithering for cascades blending +float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesState, Texture2D probesDistance, Texture2D probesIrradiance, float3 worldPosition, float3 worldNormal, float bias = 0.2f, float dither = 0.0f) { // Select the highest cascade that contains the sample location uint cascadeIndex = 0; @@ -145,7 +147,7 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesState, Textur uint probeIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, probeCoords); float4 probeState = probesState.Load(int3(GetDDGIProbeTexelCoords(data, cascadeIndex, probeIndex), 0)); probeStates[i] = probeState; - if (probeState.w == DDGI_PROBE_STATE_ACTIVE) + if (probeState.w != DDGI_PROBE_STATE_INACTIVE) activeCount++; } @@ -156,13 +158,14 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesState, Textur } if (cascadeIndex == data.CascadesCount) return data.FallbackIrradiance; - + float probesSpacing = data.ProbesOriginAndSpacing[cascadeIndex].w; float3 probesOrigin = data.ProbesScrollOffsets[cascadeIndex].xyz * probesSpacing + data.ProbesOriginAndSpacing[cascadeIndex].xyz; float3 probesExtent = (data.ProbesCounts - 1) * (probesSpacing * 0.5f); // Bias the world-space position to reduce artifacts - float3 surfaceBias = (worldNormal * bias) + (data.ViewDir * (bias * -4.0f)); + float3 viewDir = normalize(data.ViewPos - worldPosition); + float3 surfaceBias = (worldNormal * 0.2f + viewDir * 0.8f) * (0.75f * probesSpacing * bias); float3 biasedWorldPosition = worldPosition + surfaceBias; // Get the grid coordinates of the probe nearest the biased world position @@ -182,17 +185,18 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesState, Textur float4 probeState = probeStates[i]; if (probeState.w == DDGI_PROBE_STATE_INACTIVE) continue; + probeState.xyz *= probesSpacing; // Probe offset is [-1;1] within probes spacing float3 probeBasePosition = baseProbeWorldPosition + ((probeCoords - baseProbeCoords) * probesSpacing); float3 probePosition = probeBasePosition + probeState.xyz; // Calculate the distance and direction from the (biased and non-biased) shading point and the probe float3 worldPosToProbe = normalize(probePosition - worldPosition); float3 biasedPosToProbe = normalize(probePosition - biasedWorldPosition); - float biasedPosToProbeDist = length(probePosition - biasedWorldPosition); + float biasedPosToProbeDist = length(probePosition - biasedWorldPosition) * 0.95f; // Smooth backface test float weight = Square(dot(worldPosToProbe, worldNormal) * 0.5f + 0.5f); - + // Sample distance texture float2 octahedralCoords = GetOctahedralCoords(-biasedPosToProbe); float2 uv = GetDDGIProbeUV(data, cascadeIndex, probeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_DISTANCE); diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index 4a22bba27..75a2f68a8 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -17,8 +17,8 @@ #include "./Flax/GI/DDGI.hlsl" // This must match C++ -#define DDGI_TRACE_RAYS_LIMIT 512 // Limit of rays per-probe (runtime value can be smaller) -#define DDGI_TRACE_RAYS_GROUP_SIZE_X 32 +#define DDGI_TRACE_RAYS_PROBES_COUNT_LIMIT 4096 // Maximum amount of probes to update at once during rays tracing and blending +#define DDGI_TRACE_RAYS_LIMIT 256 // Limit of rays per-probe (runtime value can be smaller) #define DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE 8 #define DDGI_PROBE_CLASSIFY_GROUP_SIZE 32 @@ -30,6 +30,7 @@ GBufferData GBuffer; float2 Padding0; float ResetBlend; float TemporalTime; +int4 ProbeScrollClears[4]; META_CB_END META_CB_BEGIN(1, Data1) @@ -56,12 +57,19 @@ float3 GetProbeRayDirection(DDGIData data, uint rayIndex) return normalize(QuaternionRotate(data.RaysRotation, direction)); } +// Checks if the probe states are equal +bool GetProbeState(float a, float b) +{ + return abs(a - b) < 0.05f; +} + #ifdef _CS_Classify -RWTexture2D RWProbesState : register(u0); +RWTexture2D RWProbesState : register(u0); +RWByteAddressBuffer RWActiveProbes : register(u1); -Texture3D GlobalSDFTex[4] : register(t0); -Texture3D GlobalSDFMip[4] : register(t4); +Texture3D GlobalSDFTex : register(t0); +Texture3D GlobalSDFMip : register(t1); // Compute shader for updating probes state between active and inactive. META_CS(true, FEATURE_LEVEL_SM5) @@ -79,9 +87,10 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) // Load probe state and position float4 probeState = RWProbesState[probeDataCoords]; + probeState.xyz *= probesSpacing; // Probe offset is [-1;1] within probes spacing float3 probeBasePosition = GetDDGIProbeWorldPosition(DDGI, CascadeIndex, probeCoords); float3 probePosition = probeBasePosition + probeState.xyz; - probeState.w = DDGI_PROBE_STATE_ACTIVE; + float4 probeStateOld = probeState; // Use Global SDF to quickly get distance and direction to the scene geometry float sdf; @@ -127,9 +136,61 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) // Reset relocation probeState.xyz = float3(0, 0, 0); } + + // Check if probe was scrolled + int3 probeScrollClears = ProbeScrollClears[CascadeIndex].xyz; + bool wasScrolled = false; + UNROLL + for (uint planeIndex = 0; planeIndex < 3; planeIndex++) + { + int probeCount = (int)DDGI.ProbesCounts[planeIndex]; + int newCord = (int)probeCoords[planeIndex] + probeScrollClears[planeIndex]; + if (newCord < 0 || newCord >= probeCount) + { + wasScrolled = true; + } + } + + // If probe was in different location or was inactive last frame then mark it as activated + bool wasInactive = probeStateOld.w == DDGI_PROBE_STATE_INACTIVE; + bool wasRelocated = distance(probeState.xyz, probeStateOld.xyz) > 1.0f; + probeState.w = wasInactive || wasScrolled || wasRelocated ? DDGI_PROBE_STATE_ACTIVATED : DDGI_PROBE_STATE_ACTIVE; } - + + probeState.xyz /= probesSpacing; RWProbesState[probeDataCoords] = probeState; + + // Collect active probes + if (probeState.w != DDGI_PROBE_STATE_INACTIVE) + { + uint activeProbeIndex; + RWActiveProbes.InterlockedAdd(0, 1, activeProbeIndex); // Counter at 0 + RWActiveProbes.Store(activeProbeIndex * 4 + 4, DispatchThreadId.x); + } +} + +#endif + +#ifdef _CS_UpdateProbesInitArgs + +RWBuffer UpdateProbesInitArgs : register(u0); +ByteAddressBuffer ActiveProbes : register(t0); + +// Compute shader for building indirect dispatch arguments for CS_TraceRays and CS_UpdateProbes. +META_CS(true, FEATURE_LEVEL_SM5) +[numthreads(1, 1, 1)] +void CS_UpdateProbesInitArgs() +{ + uint probesCount = DDGI.ProbesCounts.x * DDGI.ProbesCounts.y * DDGI.ProbesCounts.z; + uint activeProbesCount = ActiveProbes.Load(0); + uint arg = 0; + for (uint probesOffset = 0; probesOffset < activeProbesCount; probesOffset += DDGI_TRACE_RAYS_PROBES_COUNT_LIMIT) + { + uint probesBatchSize = min(activeProbesCount - probesOffset, DDGI_TRACE_RAYS_PROBES_COUNT_LIMIT); + UpdateProbesInitArgs[arg++] = probesBatchSize; + UpdateProbesInitArgs[arg++] = 1; + UpdateProbesInitArgs[arg++] = 1; + } } #endif @@ -138,22 +199,28 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) RWTexture2D RWProbesTrace : register(u0); -Texture3D GlobalSDFTex[4] : register(t0); -Texture3D GlobalSDFMip[4] : register(t4); -ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t8); -Buffer GlobalSurfaceAtlasCulledObjects : register(t9); -Texture2D GlobalSurfaceAtlasDepth : register(t10); -Texture2D GlobalSurfaceAtlasTex : register(t11); -Texture2D ProbesState : register(t12); -TextureCube Skybox : register(t13); +Texture3D GlobalSDFTex : register(t0); +Texture3D GlobalSDFMip : register(t1); +ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t2); +ByteAddressBuffer RWGlobalSurfaceAtlasCulledObjects : register(t3); +Buffer GlobalSurfaceAtlasObjects : register(t4); +Texture2D GlobalSurfaceAtlasDepth : register(t5); +Texture2D GlobalSurfaceAtlasTex : register(t6); +Texture2D ProbesState : register(t7); +TextureCube Skybox : register(t8); +ByteAddressBuffer ActiveProbes : register(t9); // Compute shader for tracing rays for probes using Global SDF and Global Surface Atlas. META_CS(true, FEATURE_LEVEL_SM5) -[numthreads(DDGI_TRACE_RAYS_GROUP_SIZE_X, 1, 1)] +META_PERMUTATION_1(DDGI_TRACE_RAYS_COUNT=96) +META_PERMUTATION_1(DDGI_TRACE_RAYS_COUNT=128) +META_PERMUTATION_1(DDGI_TRACE_RAYS_COUNT=192) +META_PERMUTATION_1(DDGI_TRACE_RAYS_COUNT=256) +[numthreads(1, DDGI_TRACE_RAYS_COUNT, 1)] void CS_TraceRays(uint3 DispatchThreadId : SV_DispatchThreadID) { - uint rayIndex = DispatchThreadId.x; - uint probeIndex = DispatchThreadId.y + ProbeIndexOffset; + uint rayIndex = DispatchThreadId.y; + uint probeIndex = ActiveProbes.Load((DispatchThreadId.x + ProbeIndexOffset + 1) * 4); uint3 probeCoords = GetDDGIProbeCoords(DDGI, probeIndex); probeIndex = GetDDGIScrollingProbeIndex(DDGI, CascadeIndex, probeCoords); @@ -182,7 +249,7 @@ void CS_TraceRays(uint3 DispatchThreadId : SV_DispatchThreadID) // Sample Global Surface Atlas to get the lighting at the hit location float3 hitPosition = hit.GetHitPosition(trace); float surfaceThreshold = GetGlobalSurfaceAtlasThreshold(GlobalSDF, hit); - float4 surfaceColor = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasChunks, GlobalSurfaceAtlasCulledObjects, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hitPosition, -probeRayDirection, surfaceThreshold); + float4 surfaceColor = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasChunks, RWGlobalSurfaceAtlasCulledObjects, GlobalSurfaceAtlasObjects, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hitPosition, -probeRayDirection, surfaceThreshold); radiance = float4(surfaceColor.rgb, hit.HitTime); // Add some bias to prevent self occlusion artifacts in Chebyshev due to Global SDF being very incorrect in small scale @@ -197,7 +264,7 @@ void CS_TraceRays(uint3 DispatchThreadId : SV_DispatchThreadID) } // Write into probes trace results - RWProbesTrace[uint2(rayIndex, DispatchThreadId.y)] = radiance; + RWProbesTrace[uint2(rayIndex, DispatchThreadId.x)] = radiance; } #endif @@ -207,17 +274,19 @@ void CS_TraceRays(uint3 DispatchThreadId : SV_DispatchThreadID) #if DDGI_PROBE_UPDATE_MODE == 0 // Update irradiance #define DDGI_PROBE_RESOLUTION DDGI_PROBE_RESOLUTION_IRRADIANCE +groupshared float4 CachedProbesTraceRadiance[DDGI_TRACE_RAYS_LIMIT]; #else // Update distance #define DDGI_PROBE_RESOLUTION DDGI_PROBE_RESOLUTION_DISTANCE +groupshared float CachedProbesTraceDistance[DDGI_TRACE_RAYS_LIMIT]; #endif -groupshared float4 CachedProbesTraceRadiance[DDGI_TRACE_RAYS_LIMIT]; groupshared float3 CachedProbesTraceDirection[DDGI_TRACE_RAYS_LIMIT]; RWTexture2D RWOutput : register(u0); -Texture2D ProbesState : register(t0); +Texture2D ProbesState : register(t0); Texture2D ProbesTrace : register(t1); +ByteAddressBuffer ActiveProbes : register(t2); // Compute shader for updating probes irradiance or distance texture. META_CS(true, FEATURE_LEVEL_SM5) @@ -229,13 +298,9 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ // GroupThreadId.xy - coordinates of the probe texel: [0; DDGI_PROBE_RESOLUTION) // GroupId.x - index of the thread group which is probe index within a batch: [0; batchSize) // GroupIndex.x - index of the thread within a thread group: [0; DDGI_PROBE_RESOLUTION * DDGI_PROBE_RESOLUTION) - - // Get probe index and atlas location in the atlas - uint probeIndex = GroupId.x + ProbeIndexOffset; + uint probeIndex = ActiveProbes.Load((GroupId.x + ProbeIndexOffset + 1) * 4); uint3 probeCoords = GetDDGIProbeCoords(DDGI, probeIndex); probeIndex = GetDDGIScrollingProbeIndex(DDGI, CascadeIndex, probeCoords); - probeCoords = GetDDGIProbeCoords(DDGI, probeIndex); - uint2 outputCoords = GetDDGIProbeTexelCoords(DDGI, CascadeIndex, probeIndex) * (DDGI_PROBE_RESOLUTION + 2) + 1 + GroupThreadId.xy; // Skip disabled probes bool skip = false; @@ -243,6 +308,15 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ if (probeState == DDGI_PROBE_STATE_INACTIVE) skip = true; +#if DDGI_PROBE_UPDATE_MODE == 0 + uint backfacesCount = 0; + uint backfacesLimit = uint(DDGI.RaysCount * 0.1f); +#else + float probesSpacing = DDGI.ProbesOriginAndSpacing[CascadeIndex].w; + float distanceLimit = length(probesSpacing) * 1.5f; +#endif + + BRANCH if (!skip) { // Load trace rays results into shared memory to reuse across whole thread group (raysCount per thread) @@ -252,36 +326,20 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ for (uint i = 0; i < raysCount; i++) { uint rayIndex = raysStart + i; +#if DDGI_PROBE_UPDATE_MODE == 0 CachedProbesTraceRadiance[rayIndex] = ProbesTrace[uint2(rayIndex, GroupId.x)]; +#else + float rayDistance = ProbesTrace[uint2(rayIndex, GroupId.x)].w; + CachedProbesTraceDistance[rayIndex] = min(abs(rayDistance), distanceLimit); +#endif CachedProbesTraceDirection[rayIndex] = GetProbeRayDirection(DDGI, rayIndex); } } GroupMemoryBarrierWithGroupSync(); if (skip) return; - - // Clear probes that have been scrolled to a new positions - int3 probesScrollOffsets = DDGI.ProbesScrollOffsets[CascadeIndex].xyz; - int probeScrollClear = DDGI.ProbesScrollOffsets[CascadeIndex].w; - int3 probeScrollDirections = DDGI.ProbeScrollDirections[CascadeIndex].xyz; - bool scrolled = false; - UNROLL - for (uint planeIndex = 0; planeIndex < 3; planeIndex++) - { - if (probeScrollClear & (1 << planeIndex)) - { - int scrollOffset = probesScrollOffsets[planeIndex]; - int scrollDirection = probeScrollDirections[planeIndex]; - uint probeCount = DDGI.ProbesCounts[planeIndex]; - uint coord = (probeCount + (scrollDirection ? (scrollOffset - 1) : (scrollOffset % probeCount))) % probeCount; - if (probeCoords[planeIndex] == coord) - scrolled = true; - } - } - if (scrolled) - { - RWOutput[outputCoords] = float4(0, 0, 0, 0); - } + probeCoords = GetDDGIProbeCoords(DDGI, probeIndex); + uint2 outputCoords = GetDDGIProbeTexelCoords(DDGI, CascadeIndex, probeIndex) * (DDGI_PROBE_RESOLUTION + 2) + 1 + GroupThreadId.xy; // Calculate octahedral projection for probe (unwraps spherical projection into a square) float2 octahedralCoords = GetOctahedralCoords(GroupThreadId.xy, DDGI_PROBE_RESOLUTION); @@ -289,21 +347,14 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ // Loop over rays float4 result = float4(0, 0, 0, 0); -#if DDGI_PROBE_UPDATE_MODE == 0 - uint backfacesCount = 0; - uint backfacesLimit = uint(DDGI.RaysCount * 0.1f); -#else - float probesSpacing = DDGI.ProbesOriginAndSpacing[CascadeIndex].w; - float distanceLimit = length(probesSpacing) * 1.5f; -#endif LOOP for (uint rayIndex = 0; rayIndex < DDGI.RaysCount; rayIndex++) { float3 rayDirection = CachedProbesTraceDirection[rayIndex]; float rayWeight = max(dot(octahedralDirection, rayDirection), 0.0f); - float4 rayRadiance = CachedProbesTraceRadiance[rayIndex]; #if DDGI_PROBE_UPDATE_MODE == 0 + float4 rayRadiance = CachedProbesTraceRadiance[rayIndex]; if (rayRadiance.w < 0.0f) { // Count backface hits @@ -325,7 +376,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ rayWeight = pow(rayWeight, 10.0f); // Add distance (R), distance^2 (G) and weight (A) - float rayDistance = min(abs(rayRadiance.w), distanceLimit); + float rayDistance = CachedProbesTraceDistance[rayIndex]; result += float4(rayDistance * rayWeight, (rayDistance * rayDistance) * rayWeight, 0.0f, rayWeight); #endif } @@ -334,11 +385,16 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ float epsilon = (float)DDGI.RaysCount * 1e-9f; result.rgb *= 1.0f / (2.0f * max(result.a, epsilon)); - // Blend current value with the previous probe data + // Load current probe value float3 previous = RWOutput[outputCoords].rgb; + bool wasActivated = GetProbeState(probeState, DDGI_PROBE_STATE_ACTIVATED); + if (ResetBlend || wasActivated) + previous = float3(0, 0, 0); + + // Blend current value with the previous probe data float historyWeight = DDGI.ProbeHistoryWeight; //historyWeight = 0.0f; - if (ResetBlend || scrolled || dot(previous, previous) == 0) + if (ResetBlend || wasActivated || dot(previous, previous) == 0) historyWeight = 0.0f; #if DDGI_PROBE_UPDATE_MODE == 0 result *= DDGI.IndirectLightingIntensity; @@ -351,18 +407,18 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ if (irradianceDeltaMax > 0.2f) { // Reduce history weight after significant lighting change - historyWeight = max(historyWeight - 0.7f, 0.0f); + historyWeight = max(historyWeight - 0.9f, 0.0f); } if (irradianceDeltaLen > 2.0f) { // Reduce flickering during rapid brightness changes - result.rgb = previous + (irradianceDelta * 0.25f); + //result.rgb = previous + (irradianceDelta * 0.25f); } float3 resultDelta = (1.0f - historyWeight) * irradianceDelta; if (Max3(result.rgb) < Max3(previous)) resultDelta = min(max(abs(resultDelta), 1.0f / 1024.0f), abs(irradianceDelta)) * sign(resultDelta); - result = float4(previous + resultDelta, 1.0f); - //result = float4(lerp(result.rgb, previous.rgb, historyWeight), 1.0f); + //result = float4(previous + resultDelta, 1.0f); + result = float4(lerp(result.rgb, previous.rgb, historyWeight), 1.0f); #else result = float4(lerp(result.rg, previous.rg, historyWeight), 0.0f, 1.0f); #endif @@ -445,7 +501,7 @@ void CS_UpdateBorders(uint3 DispatchThreadId : SV_DispatchThreadID) #include "./Flax/Random.hlsl" #include "./Flax/LightingCommon.hlsl" -Texture2D ProbesState : register(t4); +Texture2D ProbesState : register(t4); Texture2D ProbesDistance : register(t5); Texture2D ProbesIrradiance : register(t6); @@ -467,7 +523,7 @@ void PS_IndirectLighting(Quad_VS2PS input, out float4 output : SV_Target0) } // Sample irradiance - float bias = 1.0f; + float bias = 0.2f; float dither = RandN2(input.TexCoord + TemporalTime).x; float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesState, ProbesDistance, ProbesIrradiance, gBuffer.WorldPos, gBuffer.Normal, bias, dither); diff --git a/Source/Shaders/GI/GlobalSurfaceAtlas.hlsl b/Source/Shaders/GI/GlobalSurfaceAtlas.hlsl index 5df1b5475..30c963d85 100644 --- a/Source/Shaders/GI/GlobalSurfaceAtlas.hlsl +++ b/Source/Shaders/GI/GlobalSurfaceAtlas.hlsl @@ -7,277 +7,280 @@ #define GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION 40 // Amount of chunks (in each direction) to split atlas draw distance for objects culling #define GLOBAL_SURFACE_ATLAS_CHUNKS_GROUP_SIZE 4 #define GLOBAL_SURFACE_ATLAS_TILE_DATA_STRIDE 5 // Amount of float4s per-tile -#define GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD_ENABLED 1 // Enables using tile normal threshold to prevent sampling pixels behind the view point (but might cause back artifacts) +#define GLOBAL_SURFACE_ATLAS_TILE_NORMAL_WEIGHT_ENABLED 1 // Enables using tile normal to weight the samples +#define GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD_ENABLED 0 // Enables using tile normal threshold to prevent sampling pixels behind the view point (but might cause back artifacts) #define GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD 0.05f // Cut-off value for tiles transitions blending during sampling #define GLOBAL_SURFACE_ATLAS_TILE_PROJ_PLANE_OFFSET 0.1f // Small offset to prevent clipping with the closest triangles (shifts near and far planes) struct GlobalSurfaceTile { - float4 AtlasRectUV; - float4x4 WorldToLocal; - float3 ViewBoundsSize; + float4 AtlasRectUV; + float4x4 WorldToLocal; + float3 ViewBoundsSize; }; struct GlobalSurfaceObject { - float3 BoundsPosition; - float BoundsRadius; - float4x4 WorldToLocal; - float3 Extent; - uint TileOffsets[6]; - uint DataSize; // count of float4s for object+tiles + float3 BoundsPosition; + float BoundsRadius; + float4x4 WorldToLocal; + float3 Extent; + bool UseVisibility; + uint TileOffsets[6]; + uint DataSize; // count of float4s for object+tiles }; float4 LoadGlobalSurfaceAtlasObjectBounds(Buffer objects, uint objectAddress) { - // This must match C++ - return objects.Load(objectAddress + 0); + // This must match C++ + return objects.Load(objectAddress + 0); } uint LoadGlobalSurfaceAtlasObjectDataSize(Buffer objects, uint objectAddress) { - // This must match C++ - return asuint(objects.Load(objectAddress + 1).w); + // This must match C++ + return asuint(objects.Load(objectAddress + 1).w); } GlobalSurfaceObject LoadGlobalSurfaceAtlasObject(Buffer objects, uint objectAddress) { - // This must match C++ - float4 vector0 = objects.Load(objectAddress + 0); - float4 vector1 = objects.Load(objectAddress + 1); - float4 vector2 = objects.Load(objectAddress + 2); - float4 vector3 = objects.Load(objectAddress + 3); - float4 vector4 = objects.Load(objectAddress + 4); - float4 vector5 = objects.Load(objectAddress + 5); // w unused - GlobalSurfaceObject object = (GlobalSurfaceObject)0; - object.BoundsPosition = vector0.xyz; - object.BoundsRadius = vector0.w; - object.WorldToLocal[0] = float4(vector2.xyz, 0.0f); - object.WorldToLocal[1] = float4(vector3.xyz, 0.0f); - object.WorldToLocal[2] = float4(vector4.xyz, 0.0f); - object.WorldToLocal[3] = float4(vector2.w, vector3.w, vector4.w, 1.0f); - object.Extent = vector5.xyz; - uint vector1x = asuint(vector1.x); - uint vector1y = asuint(vector1.y); - uint vector1z = asuint(vector1.z); - object.DataSize = asuint(vector1.w); - object.TileOffsets[0] = vector1x & 0xffff; - object.TileOffsets[1] = vector1x >> 16; - object.TileOffsets[2] = vector1y & 0xffff; - object.TileOffsets[3] = vector1y >> 16; - object.TileOffsets[4] = vector1z & 0xffff; - object.TileOffsets[5] = vector1z >> 16; - return object; + // This must match C++ + float4 vector0 = objects.Load(objectAddress + 0); + float4 vector1 = objects.Load(objectAddress + 1); + float4 vector2 = objects.Load(objectAddress + 2); + float4 vector3 = objects.Load(objectAddress + 3); + float4 vector4 = objects.Load(objectAddress + 4); + float4 vector5 = objects.Load(objectAddress + 5); + GlobalSurfaceObject object = (GlobalSurfaceObject)0; + object.BoundsPosition = vector0.xyz; + object.BoundsRadius = vector0.w; + object.WorldToLocal[0] = float4(vector2.xyz, 0.0f); + object.WorldToLocal[1] = float4(vector3.xyz, 0.0f); + object.WorldToLocal[2] = float4(vector4.xyz, 0.0f); + object.WorldToLocal[3] = float4(vector2.w, vector3.w, vector4.w, 1.0f); + object.Extent = vector5.xyz; + object.UseVisibility = vector5.w > 0.5f; + uint vector1x = asuint(vector1.x); + uint vector1y = asuint(vector1.y); + uint vector1z = asuint(vector1.z); + object.DataSize = asuint(vector1.w); + object.TileOffsets[0] = vector1x & 0xffff; + object.TileOffsets[1] = vector1x >> 16; + object.TileOffsets[2] = vector1y & 0xffff; + object.TileOffsets[3] = vector1y >> 16; + object.TileOffsets[4] = vector1z & 0xffff; + object.TileOffsets[5] = vector1z >> 16; + return object; } GlobalSurfaceTile LoadGlobalSurfaceAtlasTile(Buffer objects, uint tileAddress) { - // This must match C++ - float4 vector0 = objects.Load(tileAddress + 0); - float4 vector1 = objects.Load(tileAddress + 1); - float4 vector2 = objects.Load(tileAddress + 2); - float4 vector3 = objects.Load(tileAddress + 3); - float4 vector4 = objects.Load(tileAddress + 4); // w unused - GlobalSurfaceTile tile = (GlobalSurfaceTile)0; - tile.AtlasRectUV = vector0.xyzw; - tile.WorldToLocal[0] = float4(vector1.xyz, 0.0f); - tile.WorldToLocal[1] = float4(vector2.xyz, 0.0f); - tile.WorldToLocal[2] = float4(vector3.xyz, 0.0f); - tile.WorldToLocal[3] = float4(vector1.w, vector2.w, vector3.w, 1.0f); - tile.ViewBoundsSize = vector4.xyz; - return tile; + // This must match C++ + float4 vector0 = objects.Load(tileAddress + 0); + float4 vector1 = objects.Load(tileAddress + 1); + float4 vector2 = objects.Load(tileAddress + 2); + float4 vector3 = objects.Load(tileAddress + 3); + float4 vector4 = objects.Load(tileAddress + 4); // w unused + GlobalSurfaceTile tile = (GlobalSurfaceTile)0; + tile.AtlasRectUV = vector0.xyzw; + tile.WorldToLocal[0] = float4(vector1.xyz, 0.0f); + tile.WorldToLocal[1] = float4(vector2.xyz, 0.0f); + tile.WorldToLocal[2] = float4(vector3.xyz, 0.0f); + tile.WorldToLocal[3] = float4(vector1.w, vector2.w, vector3.w, 1.0f); + tile.ViewBoundsSize = vector4.xyz; + return tile; } // Global Surface Atlas data for a constant buffer struct GlobalSurfaceAtlasData { - float3 ViewPos; - float Padding0; - float Padding1; - float Resolution; - float ChunkSize; - uint ObjectsCount; + float3 ViewPos; + float Padding0; + float Padding1; + float Resolution; + float ChunkSize; + uint ObjectsCount; }; float3 SampleGlobalSurfaceAtlasTex(Texture2D atlas, float2 atlasUV, float4 bilinearWeights) { - float4 sampleX = atlas.GatherRed(SamplerLinearClamp, atlasUV); - float4 sampleY = atlas.GatherGreen(SamplerLinearClamp, atlasUV); - float4 sampleZ = atlas.GatherBlue(SamplerLinearClamp, atlasUV); - return float3(dot(sampleX, bilinearWeights), dot(sampleY, bilinearWeights), dot(sampleZ, bilinearWeights)); + float4 sampleX = atlas.GatherRed(SamplerLinearClamp, atlasUV); + float4 sampleY = atlas.GatherGreen(SamplerLinearClamp, atlasUV); + float4 sampleZ = atlas.GatherBlue(SamplerLinearClamp, atlasUV); + return float3(dot(sampleX, bilinearWeights), dot(sampleY, bilinearWeights), dot(sampleZ, bilinearWeights)); } -float4 SampleGlobalSurfaceAtlasTile(const GlobalSurfaceAtlasData data, GlobalSurfaceTile tile, Texture2D depth, Texture2D atlas, float3 worldPosition, float3 worldNormal, float surfaceThreshold) +float4 SampleGlobalSurfaceAtlasTile(const GlobalSurfaceAtlasData data, GlobalSurfaceObject object, GlobalSurfaceTile tile, Texture2D depth, Texture2D atlas, float3 worldPosition, float3 worldNormal, float surfaceThreshold) { -#if GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD_ENABLED - // Tile normal weight based on the sampling angle - float3 tileNormal = normalize(mul(worldNormal, (float3x3)tile.WorldToLocal)); - float normalWeight = saturate(dot(float3(0, 0, -1), tileNormal)); - normalWeight = (normalWeight - GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD) / (1.0f - GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD); - if (normalWeight <= 0.0f) - return 0; +#if GLOBAL_SURFACE_ATLAS_TILE_NORMAL_WEIGHT_ENABLED + // Tile normal weight based on the sampling angle + float3 tileNormal = normalize(mul(worldNormal, (float3x3)tile.WorldToLocal)); + float normalWeight = saturate(dot(float3(0, 0, -1), tileNormal)); + normalWeight = (normalWeight - GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD) / (1.0f - GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD); + if (normalWeight <= 0.0f && object.UseVisibility) + return 0; #endif - // Get tile UV and depth at the world position - float3 tilePosition = mul(float4(worldPosition, 1), tile.WorldToLocal).xyz; - float tileDepth = tilePosition.z / tile.ViewBoundsSize.z; - float2 tileUV = saturate((tilePosition.xy / tile.ViewBoundsSize.xy) + 0.5f); - tileUV.y = 1.0 - tileUV.y; - float2 atlasUV = tileUV * tile.AtlasRectUV.zw + tile.AtlasRectUV.xy; + // Get tile UV and depth at the world position + float3 tilePosition = mul(float4(worldPosition, 1), tile.WorldToLocal).xyz; + float tileDepth = tilePosition.z / tile.ViewBoundsSize.z; + float2 tileUV = saturate((tilePosition.xy / tile.ViewBoundsSize.xy) + 0.5f); + tileUV.y = 1.0 - tileUV.y; + tileUV = min(tileUV, 0.999999f); + float2 atlasUV = tileUV * tile.AtlasRectUV.zw + tile.AtlasRectUV.xy; - // Calculate bilinear weights - float2 bilinearWeightsUV = frac(atlasUV * data.Resolution + 0.5f); - float4 bilinearWeights; - bilinearWeights.x = (1.0 - bilinearWeightsUV.x) * (bilinearWeightsUV.y); - bilinearWeights.y = (bilinearWeightsUV.x) * (bilinearWeightsUV.y); - bilinearWeights.z = (bilinearWeightsUV.x) * (1 - bilinearWeightsUV.y); - bilinearWeights.w = (1 - bilinearWeightsUV.x) * (1 - bilinearWeightsUV.y); + // Calculate bilinear weights + float2 bilinearWeightsUV = frac(atlasUV * data.Resolution + 0.5f); + float4 bilinearWeights; + bilinearWeights.x = (1.0 - bilinearWeightsUV.x) * (bilinearWeightsUV.y); + bilinearWeights.y = (bilinearWeightsUV.x) * (bilinearWeightsUV.y); + bilinearWeights.z = (bilinearWeightsUV.x) * (1 - bilinearWeightsUV.y); + bilinearWeights.w = (1 - bilinearWeightsUV.x) * (1 - bilinearWeightsUV.y); - // Tile depth weight based on sample position occlusion - float4 tileZ = depth.Gather(SamplerLinearClamp, atlasUV, 0.0f); - float depthThreshold = 2.0f * surfaceThreshold / tile.ViewBoundsSize.z; - float4 depthVisibility = 1.0f; - UNROLL - for (uint i = 0; i < 4; i++) - { - depthVisibility[i] = 1.0f - saturate((abs(tileDepth - tileZ[i]) - depthThreshold) / (0.5f * depthThreshold)); - if (tileZ[i] >= 1.0f) - depthVisibility[i] = 0.0f; - } - float sampleWeight = dot(depthVisibility, bilinearWeights); -#if GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD_ENABLED - sampleWeight *= normalWeight; + // Tile depth weight based on sample position occlusion + float4 tileZ = depth.Gather(SamplerLinearClamp, atlasUV, 0.0f); + float depthThreshold = 2.0f * surfaceThreshold / tile.ViewBoundsSize.z; + float4 depthVisibility = 1.0f; + UNROLL + for (uint i = 0; i < 4; i++) + { + depthVisibility[i] = 1.0f - saturate((abs(tileDepth - tileZ[i]) - depthThreshold) / (0.5f * depthThreshold)); + if (tileZ[i] >= 1.0f) + depthVisibility[i] = 0.0f; + } + float sampleWeight = dot(depthVisibility, bilinearWeights); +#if GLOBAL_SURFACE_ATLAS_TILE_NORMAL_WEIGHT_ENABLED + if (object.UseVisibility) + sampleWeight *= normalWeight; #endif - if (sampleWeight <= 0.0f) - return 0; - bilinearWeights = depthVisibility * bilinearWeights; - //bilinearWeights = normalize(bilinearWeights); + if (sampleWeight <= 0.0f) + return 0; + bilinearWeights *= depthVisibility; + //bilinearWeights = normalize(bilinearWeights); - // Sample atlas texture - float3 sampleColor = SampleGlobalSurfaceAtlasTex(atlas, atlasUV, bilinearWeights); + // Sample atlas texture + float3 sampleColor = SampleGlobalSurfaceAtlasTex(atlas, atlasUV, bilinearWeights); - //return float4(sampleWeight.xxx, sampleWeight); - return float4(sampleColor.rgb * sampleWeight, sampleWeight); - //return float4(normalWeight.xxx, sampleWeight); + //return float4(sampleWeight.xxx, sampleWeight); + return float4(sampleColor.rgb * sampleWeight, sampleWeight); + //return float4(normalWeight.xxx, sampleWeight); } // Samples the Global Surface Atlas and returns the lighting (with opacity) at the given world location (and direction). // surfaceThreshold - Additional threshold (in world-units) between object or tile size compared with input data (error due to SDF or LOD incorrect appearance) -float4 SampleGlobalSurfaceAtlas(const GlobalSurfaceAtlasData data, ByteAddressBuffer chunks, Buffer culledObjects, Texture2D depth, Texture2D atlas, float3 worldPosition, float3 worldNormal, float surfaceThreshold = 20.0f) +float4 SampleGlobalSurfaceAtlas(const GlobalSurfaceAtlasData data, ByteAddressBuffer chunks, ByteAddressBuffer culledObjects, Buffer objects, Texture2D depth, Texture2D atlas, float3 worldPosition, float3 worldNormal, float surfaceThreshold = 20.0f) { - float4 result = float4(0, 0, 0, 0); + float4 result = float4(0, 0, 0, 0); - // Snap to the closest chunk to get culled objects - uint3 chunkCoord = (uint3)clamp(floor((worldPosition - data.ViewPos) / data.ChunkSize + (GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION * 0.5f)), 0, GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION - 1); - uint chunkAddress = (chunkCoord.z * (GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION * GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION) + chunkCoord.y * GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION + chunkCoord.x) * 4; - uint objectsStart = chunks.Load(chunkAddress); - if (objectsStart == 0) - { - // Empty chunk - return result; - } + // Snap to the closest chunk to get culled objects + uint3 chunkCoord = (uint3)clamp(floor((worldPosition - data.ViewPos) / data.ChunkSize + (GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION * 0.5f)), 0, GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION - 1); + uint chunkAddress = (chunkCoord.z * (GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION * GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION) + chunkCoord.y * GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION + chunkCoord.x) * 4; + uint objectsStart = chunks.Load(chunkAddress); + if (objectsStart == 0) + { + // Empty chunk + return result; + } - // Read objects counter - float4 chunkHeader = culledObjects[objectsStart]; - objectsStart++; - uint objectsCount = asuint(chunkHeader.x); - if (objectsCount > data.ObjectsCount) // Prevents crashing - don't know why the data is invalid here (rare issue when moving fast though scene with terrain) - return result; + // Read objects counter + uint objectsCount = culledObjects.Load(objectsStart * 4); + if (objectsCount > data.ObjectsCount) // Prevents crashing - don't know why the data is invalid here (rare issue when moving fast though scene with terrain) + return result; + objectsStart++; - // Loop over culled objects inside the chunk - LOOP - for (uint objectIndex = 0; objectIndex < objectsCount; objectIndex++) - { - // Cull point vs sphere - uint objectAddress = objectsStart; - float4 objectBounds = LoadGlobalSurfaceAtlasObjectBounds(culledObjects, objectAddress); - uint objectSize = LoadGlobalSurfaceAtlasObjectDataSize(culledObjects, objectAddress); - objectsStart += objectSize; - if (distance(objectBounds.xyz, worldPosition) > objectBounds.w) - continue; - GlobalSurfaceObject object = LoadGlobalSurfaceAtlasObject(culledObjects, objectAddress); - float3 localPosition = mul(float4(worldPosition, 1), object.WorldToLocal).xyz; - float3 localExtent = object.Extent + surfaceThreshold; - if (any(localPosition > localExtent) || any(localPosition < -localExtent)) - continue; + // Loop over culled objects inside the chunk + LOOP + for (uint objectIndex = 0; objectIndex < objectsCount; objectIndex++) + { + // Cull point vs sphere + uint objectAddress = culledObjects.Load(objectsStart * 4); + objectsStart++; + float4 objectBounds = LoadGlobalSurfaceAtlasObjectBounds(objects, objectAddress); + if (distance(objectBounds.xyz, worldPosition) > objectBounds.w) + continue; + GlobalSurfaceObject object = LoadGlobalSurfaceAtlasObject(objects, objectAddress); + float3 localPosition = mul(float4(worldPosition, 1), object.WorldToLocal).xyz; + float3 localExtent = object.Extent + surfaceThreshold; + if (any(localPosition > localExtent) || any(localPosition < -localExtent)) + continue; - // Remove the scale vector from the transformation matrix - float3x3 worldToLocal = (float3x3)object.WorldToLocal; - float scaleX = length(worldToLocal[0]); - float scaleY = length(worldToLocal[1]); - float scaleZ = length(worldToLocal[2]); - float3 invScale = float3( - scaleX > 0.00001f ? 1.0f / scaleX : 0.0f, - scaleY > 0.00001f ? 1.0f / scaleY : 0.0f, - scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f); - worldToLocal[0] *= invScale.x; - worldToLocal[1] *= invScale.y; - worldToLocal[2] *= invScale.z; + // Remove the scale vector from the transformation matrix + float3x3 worldToLocal = (float3x3)object.WorldToLocal; + float scaleX = length(worldToLocal[0]); + float scaleY = length(worldToLocal[1]); + float scaleZ = length(worldToLocal[2]); + float3 invScale = float3( + scaleX > 0.00001f ? 1.0f / scaleX : 0.0f, + scaleY > 0.00001f ? 1.0f / scaleY : 0.0f, + scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f); + worldToLocal[0] *= invScale.x; + worldToLocal[1] *= invScale.y; + worldToLocal[2] *= invScale.z; - // Sample tiles based on the directionality + // Sample tiles based on the directionality #if GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD_ENABLED float3 localNormal = normalize(mul(worldNormal, worldToLocal)); float3 localNormalSq = localNormal * localNormal; uint tileOffset = object.TileOffsets[localNormal.x > 0.0f ? 0 : 1]; if (localNormalSq.x > GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD * GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD && tileOffset != 0) { - GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(culledObjects, objectAddress + tileOffset); - result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); + GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset); + result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); } tileOffset = object.TileOffsets[localNormal.y > 0.0f ? 2 : 3]; if (localNormalSq.y > GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD * GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD && tileOffset != 0) { - GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(culledObjects, objectAddress + tileOffset); - result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); + GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset); + result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); } tileOffset = object.TileOffsets[localNormal.z > 0.0f ? 4 : 5]; if (localNormalSq.z > GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD * GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD && tileOffset != 0) { - GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(culledObjects, objectAddress + tileOffset); - result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); + GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset); + result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); } #else - uint tileOffset = object.TileOffsets[0]; - if (tileOffset != 0) - { - GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(culledObjects, objectAddress + tileOffset); - result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); - } - tileOffset = object.TileOffsets[1]; - if (tileOffset != 0) - { - GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(culledObjects, objectAddress + tileOffset); - result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); - } - tileOffset = object.TileOffsets[2]; - if (tileOffset != 0) - { - GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(culledObjects, objectAddress + tileOffset); - result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); - } - tileOffset = object.TileOffsets[3]; - if (tileOffset != 0) - { - GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(culledObjects, objectAddress + tileOffset); - result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); - } - tileOffset = object.TileOffsets[4]; - if (tileOffset != 0) - { - GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(culledObjects, objectAddress + tileOffset); - result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); - } - tileOffset = object.TileOffsets[5]; - if (tileOffset != 0) - { - GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(culledObjects, objectAddress + tileOffset); - result += SampleGlobalSurfaceAtlasTile(data, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); - } + uint tileOffset = object.TileOffsets[0]; + if (tileOffset != 0) + { + GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset); + result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); + } + tileOffset = object.TileOffsets[1]; + if (tileOffset != 0) + { + GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset); + result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); + } + tileOffset = object.TileOffsets[2]; + if (tileOffset != 0) + { + GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset); + result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); + } + tileOffset = object.TileOffsets[3]; + if (tileOffset != 0) + { + GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset); + result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); + } + tileOffset = object.TileOffsets[4]; + if (tileOffset != 0) + { + GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset); + result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); + } + tileOffset = object.TileOffsets[5]; + if (tileOffset != 0) + { + GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset); + result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold); + } #endif - } + } - // Normalize result - result.rgb /= max(result.a, 0.0001f); + // Normalize result + result.rgb /= max(result.a, 0.0001f); - return result; + return result; } diff --git a/Source/Shaders/GI/GlobalSurfaceAtlas.shader b/Source/Shaders/GI/GlobalSurfaceAtlas.shader index 7ae54ed83..4bfdf168d 100644 --- a/Source/Shaders/GI/GlobalSurfaceAtlas.shader +++ b/Source/Shaders/GI/GlobalSurfaceAtlas.shader @@ -62,6 +62,22 @@ void PS_Clear(out float4 Light : SV_Target0, out float4 RT0 : SV_Target1, out fl RT2 = float4(1, 0, 0, 0); } +#ifdef _PS_ClearLighting + +Buffer GlobalSurfaceAtlasObjects : register(t4); +Texture2D Texture : register(t7); + +// Pixel shader for Global Surface Atlas clearing +META_PS(true, FEATURE_LEVEL_SM5) +float4 PS_ClearLighting(AtlasVertexOutput input) : SV_Target +{ + GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(GlobalSurfaceAtlasObjects, input.TileAddress); + float2 atlasUV = input.TileUV * tile.AtlasRectUV.zw + tile.AtlasRectUV.xy; + return Texture.Sample(SamplerPointClamp, atlasUV); +} + +#endif + #ifdef _PS_Lighting #include "./Flax/GBuffer.hlsl" @@ -71,15 +87,15 @@ void PS_Clear(out float4 Light : SV_Target0, out float4 RT0 : SV_Target1, out fl // GBuffer+Depth at 0-3 slots Buffer GlobalSurfaceAtlasObjects : register(t4); #if INDIRECT_LIGHT -Texture2D ProbesState : register(t5); +Texture2D ProbesState : register(t5); Texture2D ProbesDistance : register(t6); Texture2D ProbesIrradiance : register(t7); #else -Texture3D GlobalSDFTex[4] : register(t5); -Texture3D GlobalSDFMip[4] : register(t9); +Texture3D GlobalSDFTex : register(t5); +Texture3D GlobalSDFMip : register(t6); #endif -// Pixel shader for Global Surface Atlas shading with direct light contribution +// Pixel shader for Global Surface Atlas shading META_PS(true, FEATURE_LEVEL_SM5) META_PERMUTATION_1(RADIAL_LIGHT=0) META_PERMUTATION_1(RADIAL_LIGHT=1) @@ -125,7 +141,7 @@ float4 PS_Lighting(AtlasVertexOutput input) : SV_Target // Calculate lighting float3 diffuseColor = GetDiffuseColor(gBuffer); - diffuseColor = min(diffuseColor, 0.9f); // Nothing reflects diffuse like perfectly in the real world (ensure to have energy loss at each light bounce) + diffuseColor = min(diffuseColor, 0.98f); // Nothing reflects diffuse like perfectly in the real world (ensure to have energy loss at each light bounce) float3 diffuse = Diffuse_Lambert(diffuseColor); float4 light = float4(diffuse * irradiance * gBuffer.AO, 1); #else @@ -159,7 +175,7 @@ float4 PS_Lighting(AtlasVertexOutput input) : SV_Target // Shot a ray from texel into the light to see if there is any occluder GlobalSDFTrace trace; trace.Init(gBuffer.WorldPos + gBuffer.Normal * shadowBias, L, bias, toLightDst - bias); - GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace); + GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace, 2.0f); shadowMask = hit.IsHit() ? LightShadowsStrength : 1; } else @@ -187,24 +203,25 @@ float4 PS_Lighting(AtlasVertexOutput input) : SV_Target #include "./Flax/Collisions.hlsl" RWByteAddressBuffer RWGlobalSurfaceAtlasChunks : register(u0); -RWBuffer RWGlobalSurfaceAtlasCulledObjects : register(u1); +RWByteAddressBuffer RWGlobalSurfaceAtlasCulledObjects : register(u1); Buffer GlobalSurfaceAtlasObjects : register(t0); +#define GLOBAL_SURFACE_ATLAS_CULL_LOCAL_SIZE 32 // Amount of objects to cache locally per-thread for culling + // Compute shader for culling objects into chunks META_CS(true, FEATURE_LEVEL_SM5) [numthreads(GLOBAL_SURFACE_ATLAS_CHUNKS_GROUP_SIZE, GLOBAL_SURFACE_ATLAS_CHUNKS_GROUP_SIZE, GLOBAL_SURFACE_ATLAS_CHUNKS_GROUP_SIZE)] -void CS_CullObjects(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupThreadId : SV_GroupThreadID) +void CS_CullObjects(uint3 DispatchThreadId : SV_DispatchThreadID) { uint3 chunkCoord = DispatchThreadId; uint chunkAddress = (chunkCoord.z * (GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION * GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION) + chunkCoord.y * GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION + chunkCoord.x) * 4; - if (chunkAddress == 0) - return; // Skip chunk at 0,0,0 (used for counter) float3 chunkMin = GlobalSurfaceAtlas.ViewPos + (chunkCoord - (GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION * 0.5f)) * GlobalSurfaceAtlas.ChunkSize; float3 chunkMax = chunkMin + GlobalSurfaceAtlas.ChunkSize; - // Count objects data size in this chunk (amount of float4s) - uint objectsSize = 0, objectAddress = 0, objectsCount = 0; - // TODO: maybe cache 20-30 culled object indices in thread memory to skip culling them again when copying data (maybe reude chunk size to get smaller objects count per chunk)? + // Count objects in this chunk + uint objectAddress = 0, objectsCount = 0; + // TODO: pre-cull objects within a thread group + uint localCulledObjects[GLOBAL_SURFACE_ATLAS_CULL_LOCAL_SIZE]; LOOP for (uint objectIndex = 0; objectIndex < GlobalSurfaceAtlas.ObjectsCount; objectIndex++) { @@ -212,22 +229,22 @@ void CS_CullObjects(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_Disp uint objectSize = LoadGlobalSurfaceAtlasObjectDataSize(GlobalSurfaceAtlasObjects, objectAddress); if (BoxIntersectsSphere(chunkMin, chunkMax, objectBounds.xyz, objectBounds.w)) { - objectsSize += objectSize; + localCulledObjects[objectsCount % GLOBAL_SURFACE_ATLAS_CULL_LOCAL_SIZE] = objectAddress; objectsCount++; } objectAddress += objectSize; } - if (objectsSize == 0) + if (objectsCount == 0) { // Empty chunk RWGlobalSurfaceAtlasChunks.Store(chunkAddress, 0); return; } - objectsSize++; // Include objects count before actual objects data // Allocate object data size in the buffer uint objectsStart; - RWGlobalSurfaceAtlasChunks.InterlockedAdd(0, objectsSize, objectsStart); + uint objectsSize = objectsCount + 1; // Include objects count before actual objects data + RWGlobalSurfaceAtlasCulledObjects.InterlockedAdd(0, objectsSize, objectsStart); // Counter at 0 if (objectsStart + objectsSize > CulledObjectsCapacity) { // Not enough space in the buffer @@ -238,40 +255,52 @@ void CS_CullObjects(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_Disp // Write object data start RWGlobalSurfaceAtlasChunks.Store(chunkAddress, objectsStart); - // Write objects count before actual objects data - RWGlobalSurfaceAtlasCulledObjects[objectsStart] = float4(asfloat(objectsCount), 0, 0, 0); - objectsStart++; + // Write objects count before actual objects indices + RWGlobalSurfaceAtlasCulledObjects.Store(objectsStart * 4, objectsCount); // Copy objects data in this chunk - objectAddress = 0; - LOOP - for (uint objectIndex = 0; objectIndex < GlobalSurfaceAtlas.ObjectsCount; objectIndex++) + if (objectsCount <= GLOBAL_SURFACE_ATLAS_CULL_LOCAL_SIZE) { - float4 objectBounds = LoadGlobalSurfaceAtlasObjectBounds(GlobalSurfaceAtlasObjects, objectAddress); - uint objectSize = LoadGlobalSurfaceAtlasObjectDataSize(GlobalSurfaceAtlasObjects, objectAddress); - if (BoxIntersectsSphere(chunkMin, chunkMax, objectBounds.xyz, objectBounds.w)) - { - for (uint i = 0; i < objectSize; i++) - { - RWGlobalSurfaceAtlasCulledObjects[objectsStart + i] = GlobalSurfaceAtlasObjects[objectAddress + i]; - } - objectsStart += objectSize; - } - objectAddress += objectSize; + // Reuse locally cached objects + LOOP + for (uint objectIndex = 0; objectIndex < objectsCount; objectIndex++) + { + objectAddress = localCulledObjects[objectIndex]; + objectsStart++; + RWGlobalSurfaceAtlasCulledObjects.Store(objectsStart * 4, objectAddress); + } } + else + { + // Brute-force culling + objectAddress = 0; + LOOP + for (uint objectIndex = 0; objectIndex < GlobalSurfaceAtlas.ObjectsCount; objectIndex++) + { + float4 objectBounds = LoadGlobalSurfaceAtlasObjectBounds(GlobalSurfaceAtlasObjects, objectAddress); + uint objectSize = LoadGlobalSurfaceAtlasObjectDataSize(GlobalSurfaceAtlasObjects, objectAddress); + if (BoxIntersectsSphere(chunkMin, chunkMax, objectBounds.xyz, objectBounds.w)) + { + objectsStart++; + RWGlobalSurfaceAtlasCulledObjects.Store(objectsStart * 4, objectAddress); + } + objectAddress += objectSize; + } + } } #endif #ifdef _PS_Debug -Texture3D GlobalSDFTex[4] : register(t0); -Texture3D GlobalSDFMip[4] : register(t4); -ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t8); -Buffer GlobalSurfaceAtlasCulledObjects : register(t9); -Texture2D GlobalSurfaceAtlasDepth : register(t10); -Texture2D GlobalSurfaceAtlasTex : register(t11); -TextureCube Skybox : register(t12); +Texture3D GlobalSDFTex : register(t0); +Texture3D GlobalSDFMip : register(t1); +ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t2); +ByteAddressBuffer GlobalSurfaceAtlasCulledObjects : register(t3); +Buffer GlobalSurfaceAtlasObjects : register(t4); +Texture2D GlobalSurfaceAtlasTex : register(t5); +Texture2D GlobalSurfaceAtlasDepth : register(t6); +TextureCube Skybox : register(t7); // Pixel shader for Global Surface Atlas debug drawing META_PS(true, FEATURE_LEVEL_SM5) @@ -295,7 +324,7 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target { // Sample Global Surface Atlas at the hit location float surfaceThreshold = GetGlobalSurfaceAtlasThreshold(GlobalSDF, hit); - color = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasChunks, GlobalSurfaceAtlasCulledObjects, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hit.GetHitPosition(trace), -viewRay, surfaceThreshold).rgb; + color = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasChunks, GlobalSurfaceAtlasCulledObjects, GlobalSurfaceAtlasObjects, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hit.GetHitPosition(trace), -viewRay, surfaceThreshold).rgb; //color = hit.HitNormal * 0.5f + 0.5f; } else diff --git a/Source/Shaders/GUICommon.hlsl b/Source/Shaders/GUICommon.hlsl index d19c3e783..1f8ef377b 100644 --- a/Source/Shaders/GUICommon.hlsl +++ b/Source/Shaders/GUICommon.hlsl @@ -9,53 +9,53 @@ struct Render2DVertex { - float2 Position : POSITION0; - float2 TexCoord : TEXCOORD0; - float4 Color : COLOR0; - float4 CustomDataAndClipOrigin : TEXCOORD1; // x-per-geometry type, y-features mask, zw-clip origin - float4 ClipExtents : TEXCOORD2; + float2 Position : POSITION0; + float2 TexCoord : TEXCOORD0; + float4 Color : COLOR0; + float4 CustomDataAndClipOrigin : TEXCOORD1; // x-per-geometry type, y-features mask, zw-clip origin + float4 ClipExtents : TEXCOORD2; }; struct VS2PS { - float4 Position : SV_Position; - float4 Color : COLOR0; - float2 TexCoord : TEXCOORD0; - float2 CustomData : TEXCOORD1; - float4 ClipExtents : TEXCOORD2; - float4 ClipOriginAndPos : TEXCOORD3; + float4 Position : SV_Position; + float4 Color : COLOR0; + float2 TexCoord : TEXCOORD0; + float2 CustomData : TEXCOORD1; + float4 ClipExtents : TEXCOORD2; + float4 ClipOriginAndPos : TEXCOORD3; }; float cross2(float2 a, float2 b) { - return a.x * b.y - a.y * b.x; + return a.x * b.y - a.y * b.x; } // Given a point p and a parallelogram defined by point a and vectors b and c, determines in p is inside the parallelogram. Returns a 4-vector that can be used with the clip instruction. float4 PointInParallelogram(float2 p, float2 a, float4 bc) { - float2 o = p - a; - float invD = 1 / cross2(bc.xy, bc.zw); - float2 t = (o.x * bc.yw - o.y * bc.xz) * float2(-invD, invD); - return float4(t, float2(1, 1) - t); + float2 o = p - a; + float invD = 1 / cross2(bc.xy, bc.zw); + float2 t = (o.x * bc.yw - o.y * bc.xz) * float2(-invD, invD); + return float4(t, float2(1, 1) - t); } void PerformClipping(float2 clipOrigin, float2 windowPos, float4 clipExtents) { #if CLIPPING_ENABLE - // Clip pixels which are outside of the clipping rect - float4 clipTest = PointInParallelogram(windowPos, clipOrigin, clipExtents); + // Clip pixels which are outside of the clipping rect + float4 clipTest = PointInParallelogram(windowPos, clipOrigin, clipExtents); - // Clip pixels which are outside of the clipping rect - clip(clipTest); + // Clip pixels which are outside of the clipping rect + clip(clipTest); #endif } void PerformClipping(VS2PS input) { - PerformClipping(input.ClipOriginAndPos.xy, input.ClipOriginAndPos.zw, input.ClipExtents); + PerformClipping(input.ClipOriginAndPos.xy, input.ClipOriginAndPos.zw, input.ClipExtents); } #endif diff --git a/Source/Shaders/GammaCorrectionCommon.hlsl b/Source/Shaders/GammaCorrectionCommon.hlsl index a5ca4d53d..5681616ed 100644 --- a/Source/Shaders/GammaCorrectionCommon.hlsl +++ b/Source/Shaders/GammaCorrectionCommon.hlsl @@ -9,69 +9,69 @@ // http://gpuopen.com/optimized-reversible-tonemapper-for-resolve/ float3 FastTonemap(float3 c) { - return c * rcp(max(max(c.r, c.g), c.b) + 1.0); + return c * rcp(max(max(c.r, c.g), c.b) + 1.0); } float4 FastTonemap(float4 c) { - return float4(FastTonemap(c.rgb), c.a); + return float4(FastTonemap(c.rgb), c.a); } float3 FastTonemap(float3 c, float w) { - return c * (w * rcp(max(max(c.r, c.g), c.b) + 1.0)); + return c * (w * rcp(max(max(c.r, c.g), c.b) + 1.0)); } float4 FastTonemap(float4 c, float w) { - return float4(FastTonemap(c.rgb, w), c.a); + return float4(FastTonemap(c.rgb, w), c.a); } float3 FastTonemapInvert(float3 c) { - return c * rcp(1.0 - max(max(c.r, c.g), c.b)); + return c * rcp(1.0 - max(max(c.r, c.g), c.b)); } float4 FastTonemapInvert(float4 c) { - return float4(FastTonemapInvert(c.rgb), c.a); + return float4(FastTonemapInvert(c.rgb), c.a); } float LinearToSrgbChannel(float linearColor) { - if (linearColor < 0.00313067) - return linearColor * 12.92; - return pow(linearColor, (1.0 / 2.4)) * 1.055 - 0.055; + if (linearColor < 0.00313067) + return linearColor * 12.92; + return pow(linearColor, (1.0 / 2.4)) * 1.055 - 0.055; } float3 LinearToSrgb(float3 linearColor) { - return float3( - LinearToSrgbChannel(linearColor.r), - LinearToSrgbChannel(linearColor.g), - LinearToSrgbChannel(linearColor.b)); + return float3( + LinearToSrgbChannel(linearColor.r), + LinearToSrgbChannel(linearColor.g), + LinearToSrgbChannel(linearColor.b)); } float3 sRGBToLinear(float3 color) { - color = max(6.10352e-5, color); - return color > 0.04045 ? pow(color * (1.0 / 1.055) + 0.0521327, 2.4) : color * (1.0 / 12.92); + color = max(6.10352e-5, color); + return color > 0.04045 ? pow(color * (1.0 / 1.055) + 0.0521327, 2.4) : color * (1.0 / 12.92); } float3 LogToLinear(float3 logColor) { - const float linearRange = 14.0f; - const float linearGrey = 0.18f; - const float exposureGrey = 444.0f; - return exp2((logColor - exposureGrey / 1023.0) * linearRange) * linearGrey; + const float linearRange = 14.0f; + const float linearGrey = 0.18f; + const float exposureGrey = 444.0f; + return exp2((logColor - exposureGrey / 1023.0) * linearRange) * linearGrey; } float3 LinearToLog(float3 linearColor) { - const float linearRange = 14.0f; - const float linearGrey = 0.18f; - const float exposureGrey = 444.0f; - return saturate(log2(linearColor) / linearRange - log2(linearGrey) / linearRange + exposureGrey / 1023.0f); + const float linearRange = 14.0f; + const float linearGrey = 0.18f; + const float exposureGrey = 444.0f; + return saturate(log2(linearColor) / linearRange - log2(linearGrey) / linearRange + exposureGrey / 1023.0f); } #endif diff --git a/Source/Shaders/Gather.hlsl b/Source/Shaders/Gather.hlsl index f2e0e7124..f6540fe86 100644 --- a/Source/Shaders/Gather.hlsl +++ b/Source/Shaders/Gather.hlsl @@ -10,11 +10,11 @@ float4 TextureGatherRed(Texture2D tex, SamplerState sam, float2 uv) #if CAN_USE_GATHER return tex.GatherRed(sam, uv); #else - float x = tex.Sample(sam, uv, int2(0, 1)).x; - float y = tex.Sample(sam, uv, int2(1, 1)).x; - float z = tex.Sample(sam, uv, int2(1, 0)).x; - float w = tex.Sample(sam, uv, int2(0, 0)).x; - return float4(x, y, z, w); + float x = tex.Sample(sam, uv, int2(0, 1)).x; + float y = tex.Sample(sam, uv, int2(1, 1)).x; + float z = tex.Sample(sam, uv, int2(1, 0)).x; + float w = tex.Sample(sam, uv, int2(0, 0)).x; + return float4(x, y, z, w); #endif } @@ -23,11 +23,11 @@ float4 TextureGatherRed(Texture2DArray tex, SamplerState sam, float3 uv) #if CAN_USE_GATHER return tex.GatherRed(sam, uv); #else - float x = tex.Sample(sam, uv, int2(0, 1)).x; - float y = tex.Sample(sam, uv, int2(1, 1)).x; - float z = tex.Sample(sam, uv, int2(1, 0)).x; - float w = tex.Sample(sam, uv, int2(0, 0)).x; - return float4(x, y, z, w); + float x = tex.Sample(sam, uv, int2(0, 1)).x; + float y = tex.Sample(sam, uv, int2(1, 1)).x; + float z = tex.Sample(sam, uv, int2(1, 0)).x; + float w = tex.Sample(sam, uv, int2(0, 0)).x; + return float4(x, y, z, w); #endif } @@ -36,11 +36,11 @@ float4 TextureGatherRed(Texture2D tex, SamplerState sam, float2 uv, int2 offset) #if CAN_USE_GATHER return tex.GatherRed(sam, uv, offset); #else - float x = tex.Sample(sam, uv, offset + int2(0, 1)).x; - float y = tex.Sample(sam, uv, offset + int2(1, 1)).x; - float z = tex.Sample(sam, uv, offset + int2(1, 0)).x; - float w = tex.Sample(sam, uv, offset + int2(0, 0)).x; - return float4(x, y, z, w); + float x = tex.Sample(sam, uv, offset + int2(0, 1)).x; + float y = tex.Sample(sam, uv, offset + int2(1, 1)).x; + float z = tex.Sample(sam, uv, offset + int2(1, 0)).x; + float w = tex.Sample(sam, uv, offset + int2(0, 0)).x; + return float4(x, y, z, w); #endif } @@ -49,11 +49,11 @@ float4 TextureGatherRed(Texture2D tex, SamplerState sam, float2 uv) #if CAN_USE_GATHER return tex.GatherRed(sam, uv); #else - float x = tex.Sample(sam, uv, int2(0, 1)).x; - float y = tex.Sample(sam, uv, int2(1, 1)).x; - float z = tex.Sample(sam, uv, int2(1, 0)).x; - float w = tex.Sample(sam, uv, int2(0, 0)).x; - return float4(x, y, z, w); + float x = tex.Sample(sam, uv, int2(0, 1)).x; + float y = tex.Sample(sam, uv, int2(1, 1)).x; + float z = tex.Sample(sam, uv, int2(1, 0)).x; + float w = tex.Sample(sam, uv, int2(0, 0)).x; + return float4(x, y, z, w); #endif } @@ -62,11 +62,11 @@ float4 TextureGatherRed(Texture2D tex, SamplerState sam, float2 uv, int2 #if CAN_USE_GATHER return tex.GatherRed(sam, uv, offset); #else - float x = tex.Sample(sam, uv, offset + int2(0, 1)).x; - float y = tex.Sample(sam, uv, offset + int2(1, 1)).x; - float z = tex.Sample(sam, uv, offset + int2(1, 0)).x; - float w = tex.Sample(sam, uv, offset + int2(0, 0)).x; - return float4(x, y, z, w); + float x = tex.Sample(sam, uv, offset + int2(0, 1)).x; + float y = tex.Sample(sam, uv, offset + int2(1, 1)).x; + float z = tex.Sample(sam, uv, offset + int2(1, 0)).x; + float w = tex.Sample(sam, uv, offset + int2(0, 0)).x; + return float4(x, y, z, w); #endif } diff --git a/Source/Shaders/GlobalSignDistanceField.hlsl b/Source/Shaders/GlobalSignDistanceField.hlsl index da41b1854..6022ea20f 100644 --- a/Source/Shaders/GlobalSignDistanceField.hlsl +++ b/Source/Shaders/GlobalSignDistanceField.hlsl @@ -11,238 +11,242 @@ // Global SDF data for a constant buffer struct GlobalSDFData { - float4 CascadePosDistance[4]; - float4 CascadeVoxelSize; - float2 Padding; + float4 CascadePosDistance[4]; + float4 CascadeVoxelSize; + float2 Padding; uint CascadesCount; - float Resolution; + float Resolution; }; // Global SDF ray trace settings. struct GlobalSDFTrace { - float3 WorldPosition; - float MinDistance; - float3 WorldDirection; - float MaxDistance; - float StepScale; - bool NeedsHitNormal; + float3 WorldPosition; + float MinDistance; + float3 WorldDirection; + float MaxDistance; + float StepScale; + bool NeedsHitNormal; - void Init(float3 worldPosition, float3 worldDirection, float minDistance, float maxDistance, float stepScale = 1.0f) - { - WorldPosition = worldPosition; - WorldDirection = worldDirection; - MinDistance = minDistance; - MaxDistance = maxDistance; - StepScale = stepScale; - NeedsHitNormal = false; - } + void Init(float3 worldPosition, float3 worldDirection, float minDistance, float maxDistance, float stepScale = 1.0f) + { + WorldPosition = worldPosition; + WorldDirection = worldDirection; + MinDistance = minDistance; + MaxDistance = maxDistance; + StepScale = stepScale; + NeedsHitNormal = false; + } }; // Global SDF ray trace hit information. struct GlobalSDFHit { - float3 HitNormal; - float HitTime; - uint HitCascade; - uint StepsCount; - float HitSDF; + float3 HitNormal; + float HitTime; + uint HitCascade; + uint StepsCount; + float HitSDF; - bool IsHit() - { - return HitTime >= 0.0f; - } + bool IsHit() + { + return HitTime >= 0.0f; + } - float3 GetHitPosition(const GlobalSDFTrace trace) - { - return trace.WorldPosition + trace.WorldDirection * HitTime; - } + float3 GetHitPosition(const GlobalSDFTrace trace) + { + return trace.WorldPosition + trace.WorldDirection * HitTime; + } }; +void GetGlobalSDFCascadeUV(const GlobalSDFData data, uint cascade, float3 worldPosition, out float cascadeMaxDistance, out float3 cascadeUV, out float3 textureUV) +{ + float4 cascadePosDistance = data.CascadePosDistance[cascade]; + float3 posInCascade = worldPosition - cascadePosDistance.xyz; + cascadeMaxDistance = cascadePosDistance.w * 2; + cascadeUV = saturate(posInCascade / cascadeMaxDistance + 0.5f); + textureUV = float3(((float)cascade + cascadeUV.x) / (float)data.CascadesCount, cascadeUV.y, cascadeUV.z); // cascades are placed next to each other on X axis +} + // Samples the Global SDF and returns the distance to the closest surface (in world units) at the given world location. -float SampleGlobalSDF(const GlobalSDFData data, Texture3D tex[4], float3 worldPosition) +float SampleGlobalSDF(const GlobalSDFData data, Texture3D tex, float3 worldPosition) { - float distance = data.CascadePosDistance[3].w * 2.0f; - if (distance <= 0.0f) - return GLOBAL_SDF_WORLD_SIZE; - UNROLL - for (uint cascade = 0; cascade < data.CascadesCount; cascade++) - { - float4 cascadePosDistance = data.CascadePosDistance[cascade]; - float cascadeMaxDistance = cascadePosDistance.w * 2; - float3 posInCascade = worldPosition - cascadePosDistance.xyz; - float3 cascadeUV = posInCascade / cascadeMaxDistance + 0.5f; - float cascadeDistance = tex[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0); - if (cascadeDistance < 1.0f && !any(cascadeUV < 0) && !any(cascadeUV > 1)) - { - distance = cascadeDistance * cascadeMaxDistance; - break; - } - } - return distance; + float distance = data.CascadePosDistance[3].w * 2.0f; + if (distance <= 0.0f) + return GLOBAL_SDF_WORLD_SIZE; + for (uint cascade = 0; cascade < data.CascadesCount; cascade++) + { + float cascadeMaxDistance; + float3 cascadeUV, textureUV; + GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV); + float cascadeDistance = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); + if (cascadeDistance < 1.0f && !any(cascadeUV < 0) && !any(cascadeUV > 1)) + { + distance = cascadeDistance * cascadeMaxDistance; + break; + } + } + return distance; } // Samples the Global SDF and returns the gradient vector (derivative) at the given world location. Normalize it to get normal vector. -float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex[4], float3 worldPosition, out float distance) +float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex, float3 worldPosition, out float distance) { - float3 gradient = float3(0, 0.00001f, 0); - distance = GLOBAL_SDF_WORLD_SIZE; - if (data.CascadePosDistance[3].w <= 0.0f) - return gradient; - UNROLL - for (uint cascade = 0; cascade < data.CascadesCount; cascade++) - { - float4 cascadePosDistance = data.CascadePosDistance[cascade]; - float cascadeMaxDistance = cascadePosDistance.w * 2; - float3 posInCascade = worldPosition - cascadePosDistance.xyz; - float3 cascadeUV = posInCascade / cascadeMaxDistance + 0.5f; - float cascadeDistance = tex[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0); - if (cascadeDistance < 0.9f && !any(cascadeUV < 0) && !any(cascadeUV > 1)) - { - float texelOffset = 1.0f / data.Resolution; - float xp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x + texelOffset, cascadeUV.y, cascadeUV.z), 0).x; - float xn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x - texelOffset, cascadeUV.y, cascadeUV.z), 0).x; - float yp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y + texelOffset, cascadeUV.z), 0).x; - float yn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y - texelOffset, cascadeUV.z), 0).x; - float zp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z + texelOffset), 0).x; - float zn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z - texelOffset), 0).x; - gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeMaxDistance; - distance = cascadeDistance * cascadeMaxDistance; - break; - } - } - return gradient; + float3 gradient = float3(0, 0.00001f, 0); + distance = GLOBAL_SDF_WORLD_SIZE; + if (data.CascadePosDistance[3].w <= 0.0f) + return gradient; + for (uint cascade = 0; cascade < data.CascadesCount; cascade++) + { + float cascadeMaxDistance; + float3 cascadeUV, textureUV; + GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV); + float cascadeDistance = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); + if (cascadeDistance < 0.9f && !any(cascadeUV < 0) && !any(cascadeUV > 1)) + { + float texelOffset = 1.0f / data.Resolution; + float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; + float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; + float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; + float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; + float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; + float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; + gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeMaxDistance; + distance = cascadeDistance * cascadeMaxDistance; + break; + } + } + return gradient; } // Samples the Global SDF and returns the gradient vector (derivative) at the given world location. Normalize it to get normal vector. -float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex[4], Texture3D mips[4], float3 worldPosition, out float distance) +float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D tex, Texture3D mip, float3 worldPosition, out float distance) { - float3 gradient = float3(0, 0.00001f, 0); - distance = GLOBAL_SDF_WORLD_SIZE; - if (data.CascadePosDistance[3].w <= 0.0f) - return gradient; - float chunkSizeDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / data.Resolution; // Size of the chunk in SDF distance (0-1) - float chunkMarginDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN / data.Resolution; // Size of the chunk margin in SDF distance (0-1) - UNROLL - for (uint cascade = 0; cascade < data.CascadesCount; cascade++) - { - float4 cascadePosDistance = data.CascadePosDistance[cascade]; - float cascadeMaxDistance = cascadePosDistance.w * 2; - float3 posInCascade = worldPosition - cascadePosDistance.xyz; - float3 cascadeUV = posInCascade / cascadeMaxDistance + 0.5f; - float cascadeDistance = mips[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0); - if (cascadeDistance < chunkSizeDistance && !any(cascadeUV < 0) && !any(cascadeUV > 1)) - { - float cascadeDistanceTex = tex[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0); - if (cascadeDistanceTex < chunkMarginDistance * 2) - { - cascadeDistance = cascadeDistanceTex; - } - float texelOffset = 1.0f / data.Resolution; - float xp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x + texelOffset, cascadeUV.y, cascadeUV.z), 0).x; - float xn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x - texelOffset, cascadeUV.y, cascadeUV.z), 0).x; - float yp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y + texelOffset, cascadeUV.z), 0).x; - float yn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y - texelOffset, cascadeUV.z), 0).x; - float zp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z + texelOffset), 0).x; - float zn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z - texelOffset), 0).x; - gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeMaxDistance; - distance = cascadeDistance * cascadeMaxDistance; - break; - } - } - return gradient; + float3 gradient = float3(0, 0.00001f, 0); + distance = GLOBAL_SDF_WORLD_SIZE; + if (data.CascadePosDistance[3].w <= 0.0f) + return gradient; + float chunkSizeDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / data.Resolution; // Size of the chunk in SDF distance (0-1) + float chunkMarginDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN / data.Resolution; // Size of the chunk margin in SDF distance (0-1) + for (uint cascade = 0; cascade < data.CascadesCount; cascade++) + { + float cascadeMaxDistance; + float3 cascadeUV, textureUV; + GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV); + float cascadeDistance = mip.SampleLevel(SamplerLinearClamp, textureUV, 0); + if (cascadeDistance < chunkSizeDistance && !any(cascadeUV < 0) && !any(cascadeUV > 1)) + { + float cascadeDistanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); + if (cascadeDistanceTex < chunkMarginDistance * 2) + { + cascadeDistance = cascadeDistanceTex; + } + float texelOffset = 1.0f / data.Resolution; + float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; + float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; + float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; + float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; + float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; + float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; + gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeMaxDistance; + distance = cascadeDistance * cascadeMaxDistance; + break; + } + } + return gradient; } // Ray traces the Global SDF. -GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D tex[4], Texture3D mips[4], const GlobalSDFTrace trace) +// cascadeTraceStartBias - scales the trace start position offset (along the trace direction) by cascade voxel size (reduces artifacts on far cascades). Use it for shadow rays to prevent self-occlusion when tracing from object surface that looses quality in far cascades. +GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D tex, Texture3D mip, const GlobalSDFTrace trace, float cascadeTraceStartBias = 0.0f) { - GlobalSDFHit hit = (GlobalSDFHit)0; - hit.HitTime = -1.0f; - float chunkSizeDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / data.Resolution; // Size of the chunk in SDF distance (0-1) - float chunkMarginDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN / data.Resolution; // Size of the chunk margin in SDF distance (0-1) - float nextIntersectionStart = 0.0f; - float traceMaxDistance = min(trace.MaxDistance, data.CascadePosDistance[3].w * 2); - float3 traceEndPosition = trace.WorldPosition + trace.WorldDirection * traceMaxDistance; - UNROLL - for (uint cascade = 0; cascade < data.CascadesCount && hit.HitTime < 0.0f; cascade++) - { - float4 cascadePosDistance = data.CascadePosDistance[cascade]; - float cascadeMaxDistance = cascadePosDistance.w * 2; - float voxelSize = data.CascadeVoxelSize[cascade]; - float voxelExtent = voxelSize * 0.5f; - float cascadeMinStep = voxelSize; + GlobalSDFHit hit = (GlobalSDFHit)0; + hit.HitTime = -1.0f; + float chunkSizeDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / data.Resolution; // Size of the chunk in SDF distance (0-1) + float chunkMarginDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN / data.Resolution; // Size of the chunk margin in SDF distance (0-1) + float nextIntersectionStart = 0.0f; + float traceMaxDistance = min(trace.MaxDistance, data.CascadePosDistance[3].w * 2); + float3 traceEndPosition = trace.WorldPosition + trace.WorldDirection * traceMaxDistance; + for (uint cascade = 0; cascade < data.CascadesCount && hit.HitTime < 0.0f; cascade++) + { + float4 cascadePosDistance = data.CascadePosDistance[cascade]; + float voxelSize = data.CascadeVoxelSize[cascade]; + float voxelExtent = voxelSize * 0.5f; + float cascadeMinStep = voxelSize; + float3 worldPosition = trace.WorldPosition + trace.WorldDirection * (voxelSize * cascadeTraceStartBias); - // Hit the cascade bounds to find the intersection points - float2 intersections = LineHitBox(trace.WorldPosition, traceEndPosition, cascadePosDistance.xyz - cascadePosDistance.www, cascadePosDistance.xyz + cascadePosDistance.www); - intersections.xy *= traceMaxDistance; - intersections.x = max(intersections.x, nextIntersectionStart); - float stepTime = intersections.x; - if (intersections.x >= intersections.y) - { - // Skip the current cascade if the ray starts outside it - stepTime = intersections.y; - } - else - { - // Skip the current cascade tracing on the next cascade - nextIntersectionStart = intersections.y; - } + // Hit the cascade bounds to find the intersection points + float2 intersections = LineHitBox(worldPosition, traceEndPosition, cascadePosDistance.xyz - cascadePosDistance.www, cascadePosDistance.xyz + cascadePosDistance.www); + intersections.xy *= traceMaxDistance; + intersections.x = max(intersections.x, nextIntersectionStart); + float stepTime = intersections.x; + if (intersections.x >= intersections.y) + { + // Skip the current cascade if the ray starts outside it + stepTime = intersections.y; + } + else + { + // Skip the current cascade tracing on the next cascade + nextIntersectionStart = intersections.y; + } - // Walk over the cascade SDF - uint step = 0; - LOOP - for (; step < 250 && stepTime < intersections.y; step++) - { - float3 stepPosition = trace.WorldPosition + trace.WorldDirection * stepTime; + // Walk over the cascade SDF + uint step = 0; + LOOP + for (; step < 250 && stepTime < intersections.y; step++) + { + float3 stepPosition = worldPosition + trace.WorldDirection * stepTime; - // Sample SDF - float3 posInCascade = stepPosition - cascadePosDistance.xyz; - float3 cascadeUV = posInCascade / cascadeMaxDistance + 0.5f; - float stepDistance = mips[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0); - if (stepDistance < chunkSizeDistance) - { - float stepDistanceTex = tex[cascade].SampleLevel(SamplerLinearClamp, cascadeUV, 0); - if (stepDistanceTex < chunkMarginDistance * 2) - { - stepDistance = stepDistanceTex; - } - } - else - { - // Assume no SDF nearby so perform a jump - stepDistance = chunkSizeDistance; - } - stepDistance *= cascadeMaxDistance; + // Sample SDF + float cascadeMaxDistance; + float3 cascadeUV, textureUV; + GetGlobalSDFCascadeUV(data, cascade, stepPosition, cascadeMaxDistance, cascadeUV, textureUV); + float stepDistance = mip.SampleLevel(SamplerLinearClamp, textureUV, 0); + if (stepDistance < chunkSizeDistance) + { + float stepDistanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); + if (stepDistanceTex < chunkMarginDistance * 2) + { + stepDistance = stepDistanceTex; + } + } + else + { + // Assume no SDF nearby so perform a jump + stepDistance = chunkSizeDistance; + } + stepDistance *= cascadeMaxDistance; - // Detect surface hit - float minSurfaceThickness = voxelExtent * saturate(stepTime / (voxelExtent * 2.0f)); - if (stepDistance < minSurfaceThickness) - { - // Surface hit - hit.HitTime = max(stepTime + stepDistance - minSurfaceThickness, 0.0f); - hit.HitCascade = cascade; - hit.HitSDF = stepDistance; - if (trace.NeedsHitNormal) - { - // Calculate hit normal from SDF gradient - float texelOffset = 1.0f / data.Resolution; - float xp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x + texelOffset, cascadeUV.y, cascadeUV.z), 0).x; - float xn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x - texelOffset, cascadeUV.y, cascadeUV.z), 0).x; - float yp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y + texelOffset, cascadeUV.z), 0).x; - float yn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y - texelOffset, cascadeUV.z), 0).x; - float zp = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z + texelOffset), 0).x; - float zn = tex[cascade].SampleLevel(SamplerLinearClamp, float3(cascadeUV.x, cascadeUV.y, cascadeUV.z - texelOffset), 0).x; - hit.HitNormal = normalize(float3(xp - xn, yp - yn, zp - zn)); - } - break; - } + // Detect surface hit + float minSurfaceThickness = voxelExtent * saturate(stepTime / (voxelExtent * 2.0f)); + if (stepDistance < minSurfaceThickness) + { + // Surface hit + hit.HitTime = max(stepTime + stepDistance - minSurfaceThickness, 0.0f); + hit.HitCascade = cascade; + hit.HitSDF = stepDistance; + if (trace.NeedsHitNormal) + { + // Calculate hit normal from SDF gradient + float texelOffset = 1.0f / data.Resolution; + float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; + float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; + float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; + float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; + float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; + float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; + hit.HitNormal = normalize(float3(xp - xn, yp - yn, zp - zn)); + } + break; + } - // Move forward - stepTime += max(stepDistance * trace.StepScale, cascadeMinStep); - } - hit.StepsCount += step; - } - return hit; + // Move forward + stepTime += max(stepDistance * trace.StepScale, cascadeMinStep); + } + hit.StepsCount += step; + } + return hit; } // Calculates the surface threshold for Global Surface Atlas sampling which matches the Global SDF trace to reduce artifacts diff --git a/Source/Shaders/GlobalSignDistanceField.shader b/Source/Shaders/GlobalSignDistanceField.shader index 4c2ad666e..5f644dec8 100644 --- a/Source/Shaders/GlobalSignDistanceField.shader +++ b/Source/Shaders/GlobalSignDistanceField.shader @@ -37,11 +37,15 @@ float3 CascadeCoordToPosMul; int ObjectsCount; float3 CascadeCoordToPosAdd; int CascadeResolution; -float Padding0; +int CascadeIndex; float CascadeVoxelSize; int CascadeMipResolution; int CascadeMipFactor; uint4 Objects[GLOBAL_SDF_RASTERIZE_MODEL_MAX_COUNT / 4]; +uint GenerateMipTexResolution; +uint GenerateMipCoordScale; +uint GenerateMipTexOffsetX; +uint GenerateMipMipOffsetX; META_CB_END float CombineDistanceToSDF(float sdf, float distanceToSDF) @@ -97,10 +101,11 @@ META_CS(true, FEATURE_LEVEL_SM5) META_PERMUTATION_1(READ_SDF=0) META_PERMUTATION_1(READ_SDF=1) [numthreads(GLOBAL_SDF_RASTERIZE_GROUP_SIZE, GLOBAL_SDF_RASTERIZE_GROUP_SIZE, GLOBAL_SDF_RASTERIZE_GROUP_SIZE)] -void CS_RasterizeModel(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupThreadId : SV_GroupThreadID) +void CS_RasterizeModel(uint3 DispatchThreadId : SV_DispatchThreadID) { uint3 voxelCoord = ChunkCoord + DispatchThreadId; float3 voxelWorldPos = voxelCoord * CascadeCoordToPosMul + CascadeCoordToPosAdd; + voxelCoord.x += CascadeIndex * CascadeResolution; float minDistance = MaxDistance; #if READ_SDF minDistance *= GlobalSDFTex[voxelCoord]; @@ -123,10 +128,11 @@ Texture2D ObjectsTextures[GLOBAL_SDF_RASTERIZE_HEIGHTFIELD_MAX_COUNT] : // Compute shader for rasterizing heightfield into Global SDF META_CS(true, FEATURE_LEVEL_SM5) [numthreads(GLOBAL_SDF_RASTERIZE_GROUP_SIZE, GLOBAL_SDF_RASTERIZE_GROUP_SIZE, GLOBAL_SDF_RASTERIZE_GROUP_SIZE)] -void CS_RasterizeHeightfield(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupThreadId : SV_GroupThreadID) +void CS_RasterizeHeightfield(uint3 DispatchThreadId : SV_DispatchThreadID) { uint3 voxelCoord = ChunkCoord + DispatchThreadId; float3 voxelWorldPos = voxelCoord * CascadeCoordToPosMul + CascadeCoordToPosAdd; + voxelCoord.x += CascadeIndex * CascadeResolution; float minDistance = MaxDistance * GlobalSDFTex[voxelCoord]; float thickness = CascadeVoxelSize * -8; for (uint i = 0; i < ObjectsCount; i++) @@ -167,9 +173,10 @@ RWTexture3D GlobalSDFTex : register(u0); // Compute shader for clearing Global SDF chunk META_CS(true, FEATURE_LEVEL_SM5) [numthreads(GLOBAL_SDF_RASTERIZE_GROUP_SIZE, GLOBAL_SDF_RASTERIZE_GROUP_SIZE, GLOBAL_SDF_RASTERIZE_GROUP_SIZE)] -void CS_ClearChunk(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupThreadId : SV_GroupThreadID) +void CS_ClearChunk(uint3 DispatchThreadId : SV_DispatchThreadID) { uint3 voxelCoord = ChunkCoord + DispatchThreadId; + voxelCoord.x += CascadeIndex * CascadeResolution; GlobalSDFTex[voxelCoord] = 1.0f; } @@ -182,21 +189,13 @@ Texture3D GlobalSDFTex : register(t0); float SampleSDF(uint3 voxelCoordMip, int3 offset) { -#if SAMPLE_MIP - // Sampling Global SDF Mip - float resolution = CascadeMipResolution; -#else - // Sampling Global SDF Tex - voxelCoordMip *= CascadeMipFactor; - float resolution = CascadeResolution; -#endif - // Sample SDF - voxelCoordMip = (uint3)clamp((int3)voxelCoordMip + offset, 0, resolution - 1); + voxelCoordMip = (uint3)clamp((int3)voxelCoordMip * GenerateMipCoordScale + offset, 0, GenerateMipTexResolution - 1); + voxelCoordMip.x += GenerateMipTexOffsetX; float result = GlobalSDFTex[voxelCoordMip].r; // Extend by distance to the sampled texel location - float distanceInWorldUnits = length(offset) * (MaxDistance / resolution); + float distanceInWorldUnits = length(offset) * (MaxDistance / (float)GenerateMipTexResolution); float distanceToVoxel = distanceInWorldUnits / MaxDistance; result = CombineDistanceToSDF(result, distanceToVoxel); @@ -205,10 +204,8 @@ float SampleSDF(uint3 voxelCoordMip, int3 offset) // Compute shader for generating mip for Global SDF (uses flood fill algorithm) META_CS(true, FEATURE_LEVEL_SM5) -META_PERMUTATION_1(SAMPLE_MIP=0) -META_PERMUTATION_1(SAMPLE_MIP=1) [numthreads(GLOBAL_SDF_MIP_GROUP_SIZE, GLOBAL_SDF_MIP_GROUP_SIZE, GLOBAL_SDF_MIP_GROUP_SIZE)] -void CS_GenerateMip(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupThreadId : SV_GroupThreadID) +void CS_GenerateMip(uint3 DispatchThreadId : SV_DispatchThreadID) { uint3 voxelCoordMip = DispatchThreadId; float minDistance = SampleSDF(voxelCoordMip, int3(0, 0, 0)); @@ -221,6 +218,7 @@ void CS_GenerateMip(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_Disp minDistance = min(minDistance, SampleSDF(voxelCoordMip, int3(0, -1, 0))); minDistance = min(minDistance, SampleSDF(voxelCoordMip, int3(0, 0, -1))); + voxelCoordMip.x += GenerateMipMipOffsetX; GlobalSDFMip[voxelCoordMip] = minDistance; } @@ -228,8 +226,8 @@ void CS_GenerateMip(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_Disp #ifdef _PS_Debug -Texture3D GlobalSDFTex[4] : register(t0); -Texture3D GlobalSDFMip[4] : register(t4); +Texture3D GlobalSDFTex : register(t0); +Texture3D GlobalSDFMip : register(t1); // Pixel shader for Global SDF debug drawing META_PS(true, FEATURE_LEVEL_SM5) @@ -241,6 +239,7 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target float mip = 0; uint cascade = 0; float distance01 = GlobalSDFTex[cascade].SampleLevel(SamplerLinearClamp, float3(input.TexCoord, zSlice), mip).x; + //float distance01 = GlobalSDFTex[cascade].SampleLevel(SamplerLinearClamp, float3((input.TexCoord.x + cascade) / (float)GlobalSDF.CascadesCount, input.TexCoord.y, zSlice), mip).x; //float distance01 = GlobalSDFMip[cascade].SampleLevel(SamplerLinearClamp, float3(input.TexCoord, zSlice), mip).x; float distance = distance01 * GlobalSDF.CascadePosDistance[cascade].w; if (abs(distance) < 1) diff --git a/Source/Shaders/IESProfile.hlsl b/Source/Shaders/IESProfile.hlsl index 53c53542d..c7351e275 100644 --- a/Source/Shaders/IESProfile.hlsl +++ b/Source/Shaders/IESProfile.hlsl @@ -6,10 +6,10 @@ // Calculate IES light profile from 1D texture float ComputeLightProfileMultiplier(Texture2D tex, float3 worldPosition, float3 lightPosition, float3 lightDirection) { - float3 l = normalize(worldPosition - lightPosition); - float d = dot(lightPosition, lightDirection); - float angle = asin(d) / PI + 0.5f; - return tex.SampleLevel(SamplerLinearClamp, float2(angle, 0), 0).r; + float3 l = normalize(worldPosition - lightPosition); + float d = dot(lightPosition, lightDirection); + float angle = asin(d) / PI + 0.5f; + return tex.SampleLevel(SamplerLinearClamp, float2(angle, 0), 0).r; } #endif diff --git a/Source/Shaders/Lighting.hlsl b/Source/Shaders/Lighting.hlsl index 9984d638a..af1b193a0 100644 --- a/Source/Shaders/Lighting.hlsl +++ b/Source/Shaders/Lighting.hlsl @@ -11,150 +11,150 @@ ShadowData GetShadow(LightData lightData, GBufferSample gBuffer, float4 shadowMask) { - ShadowData shadow; - shadow.SurfaceShadow = gBuffer.AO * shadowMask.r; - shadow.TransmissionShadow = shadowMask.g; - return shadow; + ShadowData shadow; + shadow.SurfaceShadow = gBuffer.AO * shadowMask.r; + shadow.TransmissionShadow = shadowMask.g; + return shadow; } LightingData StandardShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N) { - float3 diffuseColor = GetDiffuseColor(gBuffer); - float3 H = normalize(V + L); - float NoL = saturate(dot(N, L)); - float NoV = max(dot(N, V), 1e-5); - float NoH = saturate(dot(N, H)); - float VoH = saturate(dot(V, H)); + float3 diffuseColor = GetDiffuseColor(gBuffer); + float3 H = normalize(V + L); + float NoL = saturate(dot(N, L)); + float NoV = max(dot(N, V), 1e-5); + float NoH = saturate(dot(N, H)); + float VoH = saturate(dot(V, H)); - LightingData lighting; - lighting.Diffuse = Diffuse_Lambert(diffuseColor); + LightingData lighting; + lighting.Diffuse = Diffuse_Lambert(diffuseColor); #if NO_SPECULAR - lighting.Specular = 0; + lighting.Specular = 0; #else - float3 specularColor = GetSpecularColor(gBuffer); - float3 F = F_Schlick(specularColor, VoH); - float D = D_GGX(gBuffer.Roughness, NoH) * energy; - float Vis = Vis_SmithJointApprox(gBuffer.Roughness, NoV, NoL); - lighting.Specular = (D * Vis) * F; + float3 specularColor = GetSpecularColor(gBuffer); + float3 F = F_Schlick(specularColor, VoH); + float D = D_GGX(gBuffer.Roughness, NoH) * energy; + float Vis = Vis_SmithJointApprox(gBuffer.Roughness, NoV, NoL); + lighting.Specular = (D * Vis) * F; #endif - lighting.Transmission = 0; - return lighting; + lighting.Transmission = 0; + return lighting; } LightingData SubsurfaceShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N) { - LightingData lighting = StandardShading(gBuffer, energy, L, V, N); + LightingData lighting = StandardShading(gBuffer, energy, L, V, N); #if defined(USE_GBUFFER_CUSTOM_DATA) - // Fake effect of the light going through the material - float3 subsurfaceColor = gBuffer.CustomData.rgb; - float opacity = gBuffer.CustomData.a; - float3 H = normalize(V + L); - float inscatter = pow(saturate(dot(L, -V)), 12.1f) * lerp(3, 0.1f, opacity); - float normalContribution = saturate(dot(N, H) * opacity + 1.0f - opacity); - float backScatter = gBuffer.AO * normalContribution / (PI * 2.0f); - lighting.Transmission = lerp(backScatter, 1, inscatter) * subsurfaceColor; + // Fake effect of the light going through the material + float3 subsurfaceColor = gBuffer.CustomData.rgb; + float opacity = gBuffer.CustomData.a; + float3 H = normalize(V + L); + float inscatter = pow(saturate(dot(L, -V)), 12.1f) * lerp(3, 0.1f, opacity); + float normalContribution = saturate(dot(N, H) * opacity + 1.0f - opacity); + float backScatter = gBuffer.AO * normalContribution / (PI * 2.0f); + lighting.Transmission = lerp(backScatter, 1, inscatter) * subsurfaceColor; #endif - return lighting; + return lighting; } LightingData FoliageShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N) { - LightingData lighting = StandardShading(gBuffer, energy, L, V, N); + LightingData lighting = StandardShading(gBuffer, energy, L, V, N); #if defined(USE_GBUFFER_CUSTOM_DATA) - // Fake effect of the light going through the thin foliage - float3 subsurfaceColor = gBuffer.CustomData.rgb; - float wrapNoL = saturate((-dot(N, L) + 0.5f) / 2.25); - float VoL = dot(V, L); - float scatter = D_GGX(0.36, saturate(-VoL)); - lighting.Transmission = subsurfaceColor * (wrapNoL * scatter); + // Fake effect of the light going through the thin foliage + float3 subsurfaceColor = gBuffer.CustomData.rgb; + float wrapNoL = saturate((-dot(N, L) + 0.5f) / 2.25); + float VoL = dot(V, L); + float scatter = D_GGX(0.36, saturate(-VoL)); + lighting.Transmission = subsurfaceColor * (wrapNoL * scatter); #endif - return lighting; + return lighting; } LightingData SurfaceShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N) { - switch (gBuffer.ShadingModel) - { - case SHADING_MODEL_UNLIT: - case SHADING_MODEL_LIT: - return StandardShading(gBuffer, energy, L, V, N); - case SHADING_MODEL_SUBSURFACE: - return SubsurfaceShading(gBuffer, energy, L, V, N); - case SHADING_MODEL_FOLIAGE: - return FoliageShading(gBuffer, energy, L, V, N); - default: - return (LightingData)0; - } + switch (gBuffer.ShadingModel) + { + case SHADING_MODEL_UNLIT: + case SHADING_MODEL_LIT: + return StandardShading(gBuffer, energy, L, V, N); + case SHADING_MODEL_SUBSURFACE: + return SubsurfaceShading(gBuffer, energy, L, V, N); + case SHADING_MODEL_FOLIAGE: + return FoliageShading(gBuffer, energy, L, V, N); + default: + return (LightingData)0; + } } float4 GetSkyLightLighting(LightData lightData, GBufferSample gBuffer, TextureCube ibl) { - // Get material diffuse color - float3 diffuseColor = GetDiffuseColor(gBuffer); + // Get material diffuse color + float3 diffuseColor = GetDiffuseColor(gBuffer); - // Compute the preconvolved incoming lighting with the normal direction (apply ambient color) - // Some data is packed, see C++ RendererSkyLightData::SetupLightData - float mip = lightData.SourceLength; - float3 diffuseLookup = ibl.SampleLevel(SamplerLinearClamp, gBuffer.Normal, mip).rgb * lightData.Color.rgb; - diffuseLookup += float3(lightData.SpotAngles.rg, lightData.SourceRadius); + // Compute the preconvolved incoming lighting with the normal direction (apply ambient color) + // Some data is packed, see C++ RendererSkyLightData::SetupLightData + float mip = lightData.SourceLength; + float3 diffuseLookup = ibl.SampleLevel(SamplerLinearClamp, gBuffer.Normal, mip).rgb * lightData.Color.rgb; + diffuseLookup += float3(lightData.SpotAngles.rg, lightData.SourceRadius); - // Fade out based on distance to capture - float3 captureVector = gBuffer.WorldPos - lightData.Position; - float captureVectorLength = length(captureVector); - float normalizedDistanceToCapture = saturate(captureVectorLength / lightData.Radius); - float distanceAlpha = 1.0 - smoothstep(0.6, 1, normalizedDistanceToCapture); + // Fade out based on distance to capture + float3 captureVector = gBuffer.WorldPos - lightData.Position; + float captureVectorLength = length(captureVector); + float normalizedDistanceToCapture = saturate(captureVectorLength / lightData.Radius); + float distanceAlpha = 1.0 - smoothstep(0.6, 1, normalizedDistanceToCapture); - // Calculate final light - float3 color = diffuseLookup * diffuseColor; - float luminance = Luminance(diffuseLookup); - return float4(color, luminance) * (distanceAlpha * gBuffer.AO); + // Calculate final light + float3 color = diffuseLookup * diffuseColor; + float luminance = Luminance(diffuseLookup); + return float4(color, luminance) * (distanceAlpha * gBuffer.AO); } float4 GetLighting(float3 viewPos, LightData lightData, GBufferSample gBuffer, float4 shadowMask, bool isRadial, bool isSpotLight) { - float4 result = 0; - float3 V = normalize(viewPos - gBuffer.WorldPos); - float3 N = gBuffer.Normal; - float3 L = lightData.Direction; // no need to normalize - float NoL = saturate(dot(N, L)); - float distanceAttenuation = 1; - float lightRadiusMask = 1; - float spotAttenuation = 1; - float3 toLight = lightData.Direction; + float4 result = 0; + float3 V = normalize(viewPos - gBuffer.WorldPos); + float3 N = gBuffer.Normal; + float3 L = lightData.Direction; // no need to normalize + float NoL = saturate(dot(N, L)); + float distanceAttenuation = 1; + float lightRadiusMask = 1; + float spotAttenuation = 1; + float3 toLight = lightData.Direction; - // Calculate attenuation - if (isRadial) - { - toLight = lightData.Position - gBuffer.WorldPos; - float distanceSqr = dot(toLight, toLight); - L = toLight * rsqrt(distanceSqr); - GetRadialLightAttenuation(lightData, isSpotLight, N, distanceSqr, 1, toLight, L, NoL, distanceAttenuation, lightRadiusMask, spotAttenuation); - } - float attenuation = distanceAttenuation * lightRadiusMask * spotAttenuation; + // Calculate attenuation + if (isRadial) + { + toLight = lightData.Position - gBuffer.WorldPos; + float distanceSqr = dot(toLight, toLight); + L = toLight * rsqrt(distanceSqr); + GetRadialLightAttenuation(lightData, isSpotLight, N, distanceSqr, 1, toLight, L, NoL, distanceAttenuation, lightRadiusMask, spotAttenuation); + } + float attenuation = distanceAttenuation * lightRadiusMask * spotAttenuation; - // Calculate shadow - ShadowData shadow = GetShadow(lightData, gBuffer, shadowMask); + // Calculate shadow + ShadowData shadow = GetShadow(lightData, gBuffer, shadowMask); - // Reduce shadow mapping artifacts - shadow.SurfaceShadow *= saturate(NoL * 6.0f - 0.2f); + // Reduce shadow mapping artifacts + shadow.SurfaceShadow *= saturate(NoL * 6.0f - 0.2f); - BRANCH - if (shadow.SurfaceShadow + shadow.TransmissionShadow > 0) - { - gBuffer.Roughness = max(gBuffer.Roughness, lightData.MinRoughness); - float energy = AreaLightSpecular(lightData, gBuffer.Roughness, toLight, L, V, N); + BRANCH + if (shadow.SurfaceShadow + shadow.TransmissionShadow > 0) + { + gBuffer.Roughness = max(gBuffer.Roughness, lightData.MinRoughness); + float energy = AreaLightSpecular(lightData, gBuffer.Roughness, toLight, L, V, N); - // Calculate direct lighting - LightingData lighting = SurfaceShading(gBuffer, energy, L, V, N); + // Calculate direct lighting + LightingData lighting = SurfaceShading(gBuffer, energy, L, V, N); - // Calculate final light color - float3 surfaceLight = (lighting.Diffuse + lighting.Specular) * (NoL * attenuation * shadow.SurfaceShadow); - float3 subsurfaceLight = lighting.Transmission * (attenuation * shadow.TransmissionShadow); - result.rgb = lightData.Color * (surfaceLight + subsurfaceLight); - result.a = 1; - } + // Calculate final light color + float3 surfaceLight = (lighting.Diffuse + lighting.Specular) * (NoL * attenuation * shadow.SurfaceShadow); + float3 subsurfaceLight = lighting.Transmission * (attenuation * shadow.TransmissionShadow); + result.rgb = lightData.Color * (surfaceLight + subsurfaceLight); + result.a = 1; + } - return result; + return result; } #endif diff --git a/Source/Shaders/LightingCommon.hlsl b/Source/Shaders/LightingCommon.hlsl index 6666f0951..1015cf536 100644 --- a/Source/Shaders/LightingCommon.hlsl +++ b/Source/Shaders/LightingCommon.hlsl @@ -9,126 +9,126 @@ // Structure that contains information about light struct LightData { - float2 SpotAngles; - float SourceRadius; - float SourceLength; + float2 SpotAngles; + float SourceRadius; + float SourceLength; - float3 Color; - float MinRoughness; + float3 Color; + float MinRoughness; - float3 Position; - float CastShadows; + float3 Position; + float CastShadows; - float3 Direction; - float Radius; + float3 Direction; + float Radius; - float FalloffExponent; - float InverseSquared; - float Dummy0; - float RadiusInv; + float FalloffExponent; + float InverseSquared; + float Dummy0; + float RadiusInv; }; // Structure that contains information about shadow struct ShadowData { - float SurfaceShadow; - float TransmissionShadow; + float SurfaceShadow; + float TransmissionShadow; }; // Structure that contains information about direct lighting calculations result struct LightingData { - float3 Diffuse; - float3 Specular; - float3 Transmission; + float3 Diffuse; + float3 Specular; + float3 Transmission; }; // Calculates radial light (point or spot) attenuation factors (distance, spot and radius mask) void GetRadialLightAttenuation( - LightData lightData, - bool isSpotLight, - float3 N, - float distanceSqr, - float distanceBiasSqr, - float3 toLight, - float3 L, - inout float NoL, - inout float distanceAttenuation, - inout float lightRadiusMask, - inout float spotAttenuation) + LightData lightData, + bool isSpotLight, + float3 N, + float distanceSqr, + float distanceBiasSqr, + float3 toLight, + float3 L, + inout float NoL, + inout float distanceAttenuation, + inout float lightRadiusMask, + inout float spotAttenuation) { - if (lightData.InverseSquared) - { - BRANCH - if (lightData.SourceLength > 0) - { - float3 l01 = lightData.Direction * lightData.SourceLength; - float3 l0 = toLight - 0.5 * l01; - float3 l1 = toLight + 0.5 * l01; - float lengthL0 = length(l0); - float lengthL1 = length(l1); - distanceAttenuation = rcp((lengthL0 * lengthL1 + dot(l0, l1)) * 0.5 + distanceBiasSqr); - NoL = saturate(0.5 * (dot(N, l0) / lengthL0 + dot(N, l1) / lengthL1)); - } - else - { - distanceAttenuation = rcp(distanceSqr + distanceBiasSqr); - NoL = saturate(dot(N, L)); - } - lightRadiusMask = Square(saturate(1 - Square(distanceSqr * Square(lightData.RadiusInv)))); - } - else - { - distanceAttenuation = 1; - NoL = saturate(dot(N, L)); - float3 worldLightVector = toLight * lightData.RadiusInv; - float t = dot(worldLightVector, worldLightVector); - lightRadiusMask = pow(1.0f - saturate(t), lightData.FalloffExponent); - } + if (lightData.InverseSquared) + { + BRANCH + if (lightData.SourceLength > 0) + { + float3 l01 = lightData.Direction * lightData.SourceLength; + float3 l0 = toLight - 0.5 * l01; + float3 l1 = toLight + 0.5 * l01; + float lengthL0 = length(l0); + float lengthL1 = length(l1); + distanceAttenuation = rcp((lengthL0 * lengthL1 + dot(l0, l1)) * 0.5 + distanceBiasSqr); + NoL = saturate(0.5 * (dot(N, l0) / lengthL0 + dot(N, l1) / lengthL1)); + } + else + { + distanceAttenuation = rcp(distanceSqr + distanceBiasSqr); + NoL = saturate(dot(N, L)); + } + lightRadiusMask = Square(saturate(1 - Square(distanceSqr * Square(lightData.RadiusInv)))); + } + else + { + distanceAttenuation = 1; + NoL = saturate(dot(N, L)); + float3 worldLightVector = toLight * lightData.RadiusInv; + float t = dot(worldLightVector, worldLightVector); + lightRadiusMask = pow(1.0f - saturate(t), lightData.FalloffExponent); + } - if (isSpotLight) - { - // SpotAngles.x is CosOuterCone, SpotAngles.y is InvCosConeDifference - spotAttenuation = Square(saturate((dot(normalize(-L), lightData.Direction) - lightData.SpotAngles.x) * lightData.SpotAngles.y)); - } + if (isSpotLight) + { + // SpotAngles.x is CosOuterCone, SpotAngles.y is InvCosConeDifference + spotAttenuation = Square(saturate((dot(normalize(-L), lightData.Direction) - lightData.SpotAngles.x) * lightData.SpotAngles.y)); + } } // Find representative incoming light direction and energy modification float AreaLightSpecular(LightData lightData, float roughness, inout float3 toLight, inout float3 L, float3 V, half3 N) { - float energy = 1; + float energy = 1; - float m = roughness * roughness; - float3 r = reflect(-V, N); - float invDistToLight = rsqrt(dot(toLight, toLight)); + float m = roughness * roughness; + float3 r = reflect(-V, N); + float invDistToLight = rsqrt(dot(toLight, toLight)); - BRANCH - if (lightData.SourceLength > 0) - { - float lineAngle = saturate(lightData.SourceLength * invDistToLight); - energy *= m / saturate(m + 0.5 * lineAngle); - float3 l01 = lightData.Direction * lightData.SourceLength; - float3 l0 = toLight - 0.5 * l01; - float a = Square(lightData.SourceLength); - float b = dot(r, l01); - float t = saturate(dot(l0, b * r - l01) / (a - b * b)); - toLight = l0 + t * l01; - } + BRANCH + if (lightData.SourceLength > 0) + { + float lineAngle = saturate(lightData.SourceLength * invDistToLight); + energy *= m / saturate(m + 0.5 * lineAngle); + float3 l01 = lightData.Direction * lightData.SourceLength; + float3 l0 = toLight - 0.5 * l01; + float a = Square(lightData.SourceLength); + float b = dot(r, l01); + float t = saturate(dot(l0, b * r - l01) / (a - b * b)); + toLight = l0 + t * l01; + } - BRANCH - if (lightData.SourceRadius > 0) - { - float sphereAngle = saturate(lightData.SourceRadius * invDistToLight); - energy *= Square(m / saturate(m + 0.5 * sphereAngle)); - float3 closestPointOnRay = dot(toLight, r) * r; - float3 centerToRay = closestPointOnRay - toLight; - float3 closestPointOnSphere = toLight + centerToRay * saturate(lightData.SourceRadius * rsqrt(dot(centerToRay, centerToRay))); - toLight = closestPointOnSphere; - } + BRANCH + if (lightData.SourceRadius > 0) + { + float sphereAngle = saturate(lightData.SourceRadius * invDistToLight); + energy *= Square(m / saturate(m + 0.5 * sphereAngle)); + float3 closestPointOnRay = dot(toLight, r) * r; + float3 centerToRay = closestPointOnRay - toLight; + float3 closestPointOnSphere = toLight + centerToRay * saturate(lightData.SourceRadius * rsqrt(dot(centerToRay, centerToRay))); + toLight = closestPointOnSphere; + } - L = normalize(toLight); + L = normalize(toLight); - return energy; + return energy; } #endif diff --git a/Source/Shaders/MaterialCommon.hlsl b/Source/Shaders/MaterialCommon.hlsl index a18e29f81..60ba7e298 100644 --- a/Source/Shaders/MaterialCommon.hlsl +++ b/Source/Shaders/MaterialCommon.hlsl @@ -20,69 +20,69 @@ // Validate inputs #ifndef MATERIAL - #define MATERIAL 0 +#define MATERIAL 0 #endif #ifndef MATERIAL_DOMAIN - #define MATERIAL_DOMAIN MATERIAL_DOMAIN_SURFACE +#define MATERIAL_DOMAIN MATERIAL_DOMAIN_SURFACE #endif #ifndef MATERIAL_BLEND - #define MATERIAL_BLEND MATERIAL_BLEND_OPAQUE +#define MATERIAL_BLEND MATERIAL_BLEND_OPAQUE #endif #ifndef MATERIAL_SHADING_MODEL - #define MATERIAL_SHADING_MODEL SHADING_MODEL_LIT +#define MATERIAL_SHADING_MODEL SHADING_MODEL_LIT #endif #ifndef USE_INSTANCING - #define USE_INSTANCING 0 +#define USE_INSTANCING 0 #endif #ifndef USE_SKINNING - #define USE_SKINNING 0 +#define USE_SKINNING 0 #endif #ifndef USE_LIGHTMAP - #define USE_LIGHTMAP 0 +#define USE_LIGHTMAP 0 #endif #ifndef USE_POSITION_OFFSET - #define USE_POSITION_OFFSET 0 +#define USE_POSITION_OFFSET 0 #endif #ifndef USE_VERTEX_COLOR - #define USE_VERTEX_COLOR 0 +#define USE_VERTEX_COLOR 0 #endif #ifndef USE_DISPLACEMENT - #define USE_DISPLACEMENT 0 +#define USE_DISPLACEMENT 0 #endif #ifndef USE_TESSELLATION - #define USE_TESSELLATION 0 +#define USE_TESSELLATION 0 #endif #ifndef USE_DITHERED_LOD_TRANSITION - #define USE_DITHERED_LOD_TRANSITION 0 +#define USE_DITHERED_LOD_TRANSITION 0 #endif #ifndef MATERIAL_TESSELLATION - #define MATERIAL_TESSELLATION MATERIAL_TESSELLATION_NONE +#define MATERIAL_TESSELLATION MATERIAL_TESSELLATION_NONE #endif #ifndef MAX_TESSELLATION_FACTOR - #define MAX_TESSELLATION_FACTOR 15 +#define MAX_TESSELLATION_FACTOR 15 #endif #ifndef PER_BONE_MOTION_BLUR - #define PER_BONE_MOTION_BLUR 0 +#define PER_BONE_MOTION_BLUR 0 #endif // Material properties struct Material { - float3 Emissive; - float Roughness; - float3 Color; - float AO; - float3 WorldNormal; - float Metalness; - float3 TangentNormal; - float Specular; - float3 PositionOffset; - float Opacity; - float3 SubsurfaceColor; - float Refraction; - float Mask; - float TessellationMultiplier; - float3 WorldDisplacement; + float3 Emissive; + float Roughness; + float3 Color; + float AO; + float3 WorldNormal; + float Metalness; + float3 TangentNormal; + float Specular; + float3 PositionOffset; + float Opacity; + float3 SubsurfaceColor; + float Refraction; + float Mask; + float TessellationMultiplier; + float3 WorldDisplacement; #if USE_CUSTOM_VERTEX_INTERPOLATORS float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT]; #endif @@ -90,11 +90,11 @@ struct Material struct ModelInput { - float3 Position : POSITION; - float2 TexCoord : TEXCOORD0; - float4 Normal : NORMAL; - float4 Tangent : TANGENT; - float2 LightmapUV : TEXCOORD1; + float3 Position : POSITION; + float2 TexCoord : TEXCOORD0; + float4 Normal : NORMAL; + float4 Tangent : TANGENT; + float2 LightmapUV : TEXCOORD1; #if USE_VERTEX_COLOR half4 Color : COLOR; #endif @@ -109,7 +109,7 @@ struct ModelInput struct ModelInput_PosOnly { - float3 Position : POSITION; + float3 Position : POSITION; #if USE_INSTANCING float4 InstanceOrigin : ATTRIBUTE0; // .w contains PerInstanceRandom float4 InstanceTransform1 : ATTRIBUTE1; // .w contains LODDitherFactor @@ -121,12 +121,12 @@ struct ModelInput_PosOnly struct ModelInput_Skinned { - float3 Position : POSITION; - float2 TexCoord : TEXCOORD0; - float4 Normal : NORMAL; - float4 Tangent : TANGENT; - uint4 BlendIndices : BLENDINDICES; - float4 BlendWeights : BLENDWEIGHT; + float3 Position : POSITION; + float2 TexCoord : TEXCOORD0; + float4 Normal : NORMAL; + float4 Tangent : TANGENT; + uint4 BlendIndices : BLENDINDICES; + float4 BlendWeights : BLENDWEIGHT; #if USE_INSTANCING float4 InstanceOrigin : ATTRIBUTE0; // .w contains PerInstanceRandom float4 InstanceTransform1 : ATTRIBUTE1; // .w contains LODDitherFactor @@ -138,55 +138,55 @@ struct ModelInput_Skinned struct Model_VS2PS { - float4 Position : SV_Position; - float4 ScreenPos : TEXCOORD0; + float4 Position : SV_Position; + float4 ScreenPos : TEXCOORD0; }; struct GBufferOutput { - float4 Light : SV_Target0; - float4 RT0 : SV_Target1; - float4 RT1 : SV_Target2; - float4 RT2 : SV_Target3; - float4 RT3 : SV_Target4; + float4 Light : SV_Target0; + float4 RT0 : SV_Target1; + float4 RT1 : SV_Target2; + float4 RT2 : SV_Target3; + float4 RT3 : SV_Target4; }; float3x3 CalcTangentBasis(float3 normal, float3 pos, float2 uv) { - // References: - // http://www.thetenthplanet.de/archives/1180 - // https://zhangdoa.com/posts/normal-and-normal-mapping - float3 dp1 = ddx(pos); - float3 dp2 = ddy(pos); - float2 duv1 = ddx(uv); - float2 duv2 = ddy(uv); - float3 dp2perp = cross(dp2, normal); - float3 dp1perp = cross(normal, dp1); - float3 tangent = normalize(dp2perp * duv1.x + dp1perp * duv2.x); - float3 bitangent = normalize(dp2perp * duv1.y + dp1perp * duv2.y); - return float3x3(tangent, bitangent, normal); + // References: + // http://www.thetenthplanet.de/archives/1180 + // https://zhangdoa.com/posts/normal-and-normal-mapping + float3 dp1 = ddx(pos); + float3 dp2 = ddy(pos); + float2 duv1 = ddx(uv); + float2 duv2 = ddy(uv); + float3 dp2perp = cross(dp2, normal); + float3 dp1perp = cross(normal, dp1); + float3 tangent = normalize(dp2perp * duv1.x + dp1perp * duv2.x); + float3 bitangent = normalize(dp2perp * duv1.y + dp1perp * duv2.y); + return float3x3(tangent, bitangent, normal); } float3x3 CalcTangentBasisFromWorldNormal(float3 normal) { - float3 tangent = cross(normal, float3(1, 0, 0)); - float3 bitangent = cross(normal, tangent); - return float3x3(tangent, bitangent, normal); + float3 tangent = cross(normal, float3(1, 0, 0)); + float3 bitangent = cross(normal, tangent); + return float3x3(tangent, bitangent, normal); } float3x3 CalcTangentBasis(float3 normal, float4 tangent) { - float3 bitangent = cross(normal, tangent.xyz) * tangent.w; - return float3x3(tangent.xyz, bitangent, normal); + float3 bitangent = cross(normal, tangent.xyz) * tangent.w; + return float3x3(tangent.xyz, bitangent, normal); } // [Jimenez et al. 2016, "Practical Realtime Strategies for Accurate Indirect Occlusion"] float3 AOMultiBounce(float visibility, float3 albedo) { - float3 a = 2.0404 * albedo - 0.3324; - float3 b = -4.7951 * albedo + 0.6417; - float3 c = 2.7552 * albedo + 0.6903; - return max(visibility, ((visibility * a + b) * visibility + c) * visibility); + float3 a = 2.0404 * albedo - 0.3324; + float3 b = -4.7951 * albedo + 0.6417; + float3 c = 2.7552 * albedo + 0.6903; + return max(visibility, ((visibility * a + b) * visibility + c) * visibility); } #endif diff --git a/Source/Shaders/Math.hlsl b/Source/Shaders/Math.hlsl index 037efccba..16a4ff549 100644 --- a/Source/Shaders/Math.hlsl +++ b/Source/Shaders/Math.hlsl @@ -5,254 +5,254 @@ uint NextPow2(uint value) { - uint mask = (1 << firstbithigh(value)) - 1; - return (value + mask) & ~mask; + uint mask = (1 << firstbithigh(value)) - 1; + return (value + mask) & ~mask; } float3 SafeNormalize(float3 v) { - return v / sqrt(max(dot(v, v), 0.01)); + return v / sqrt(max(dot(v, v), 0.01)); } float3 ExtractLargestComponent(float3 v) { - float3 a = abs(v); - if (a.x > a.y) - { - if (a.x > a.z) - { - return float3(v.x > 0 ? 1 : -1, 0, 0); - } - } - else - { - if (a.y > a.z) - { - return float3(0, v.y > 0 ? 1 : -1, 0); - } - } - return float3(0, 0, v.z > 0 ? 1 : -1); + float3 a = abs(v); + if (a.x > a.y) + { + if (a.x > a.z) + { + return float3(v.x > 0 ? 1 : -1, 0, 0); + } + } + else + { + if (a.y > a.z) + { + return float3(0, v.y > 0 ? 1 : -1, 0); + } + } + return float3(0, 0, v.z > 0 ? 1 : -1); } float Square(float x) { - return x * x; + return x * x; } float2 Square(float2 x) { - return x * x; + return x * x; } float3 Square(float3 x) { - return x * x; + return x * x; } float4 Square(float4 x) { - return x * x; + return x * x; } float Min2(float2 x) { - return min(x.x, x.y); + return min(x.x, x.y); } float Min3(float3 x) { - return min(x.x, min(x.y, x.z)); + return min(x.x, min(x.y, x.z)); } float Min4(float4 x) { - return min(x.x, min(x.y, min(x.z, x.w))); + return min(x.x, min(x.y, min(x.z, x.w))); } float Max2(float2 x) { - return max(x.x, x.y); + return max(x.x, x.y); } float Max3(float3 x) { - return max(x.x, max(x.y, x.z)); + return max(x.x, max(x.y, x.z)); } float Max4(float4 x) { - return max(x.x, max(x.y, max(x.z, x.w))); + return max(x.x, max(x.y, max(x.z, x.w))); } float Pow2(float x) { - return x * x; + return x * x; } float2 Pow2(float2 x) { - return x * x; + return x * x; } float3 Pow2(float3 x) { - return x * x; + return x * x; } float4 Pow2(float4 x) { - return x * x; + return x * x; } float Pow3(float x) { - return x * x * x; + return x * x * x; } float2 Pow3(float2 x) { - return x * x * x; + return x * x * x; } float3 Pow3(float3 x) { - return x * x * x; + return x * x * x; } float4 Pow3(float4 x) { - return x * x * x; + return x * x * x; } float Pow4(float x) { - float xx = x * x; - return xx * xx; + float xx = x * x; + return xx * xx; } float2 Pow4(float2 x) { - float2 xx = x * x; - return xx * xx; + float2 xx = x * x; + return xx * xx; } float3 Pow4(float3 x) { - float3 xx = x * x; - return xx * xx; + float3 xx = x * x; + return xx * xx; } float4 Pow4(float4 x) { - float4 xx = x * x; - return xx * xx; + float4 xx = x * x; + return xx * xx; } float Pow5(float x) { - float xx = x * x; - return xx * xx * x; + float xx = x * x; + return xx * xx * x; } float2 Pow5(float2 x) { - float2 xx = x * x; - return xx * xx * x; + float2 xx = x * x; + return xx * xx * x; } float3 Pow5(float3 x) { - float3 xx = x * x; - return xx * xx * x; + float3 xx = x * x; + return xx * xx * x; } float4 Pow5(float4 x) { - float4 xx = x * x; - return xx * xx * x; + float4 xx = x * x; + return xx * xx * x; } float Pow6(float x) { - float xx = x * x; - return xx * xx * xx; + float xx = x * x; + return xx * xx * xx; } float2 Pow6(float2 x) { - float2 xx = x * x; - return xx * xx * xx; + float2 xx = x * x; + return xx * xx * xx; } float3 Pow6(float3 x) { - float3 xx = x * x; - return xx * xx * xx; + float3 xx = x * x; + return xx * xx * xx; } float4 Pow6(float4 x) { - float4 xx = x * x; - return xx * xx * xx; + float4 xx = x * x; + return xx * xx * xx; } float ClampedPow(float x, float y) { - return pow(max(abs(x), 0.000001f), y); + return pow(max(abs(x), 0.000001f), y); } float2 ClampedPow(float2 x, float2 y) { - return pow(max(abs(x), float2(0.000001f, 0.000001f)), y); + return pow(max(abs(x), float2(0.000001f, 0.000001f)), y); } float3 ClampedPow(float3 x, float3 y) { - return pow(max(abs(x), float3(0.000001f, 0.000001f, 0.000001f)), y); + return pow(max(abs(x), float3(0.000001f, 0.000001f, 0.000001f)), y); } float4 ClampedPow(float4 x, float4 y) { - return pow(max(abs(x), float4(0.000001f, 0.000001f, 0.000001f, 0.000001f)), y); + return pow(max(abs(x), float4(0.000001f, 0.000001f, 0.000001f, 0.000001f)), y); } float4 FindQuatBetween(float3 from, float3 to) { - float normAB = 1.0f; - float w = normAB + dot(from, to); - float4 result; + float normAB = 1.0f; + float w = normAB + dot(from, to); + float4 result; - if (w >= 1e-6f * normAB) - { - result = float4 - ( - from.y * to.z - from.z * to.y, - from.z * to.x - from.x * to.z, - from.x * to.y - from.y * to.x, - w - ); - } - else - { - w = 0.f; - result = abs(from.x) > abs(from.y) - ? float4(-from.z, 0.f, from.x, w) - : float4(0.f, -from.z, from.y, w); - } + if (w >= 1e-6f * normAB) + { + result = float4 + ( + from.y * to.z - from.z * to.y, + from.z * to.x - from.x * to.z, + from.x * to.y - from.y * to.x, + w + ); + } + else + { + w = 0.f; + result = abs(from.x) > abs(from.y) + ? float4(-from.z, 0.f, from.x, w) + : float4(0.f, -from.z, from.y, w); + } - return normalize(result); + return normalize(result); } // Rotates Position about the given axis by the given angle, in radians, and returns the offset to position float3 RotateAboutAxis(float4 normalizedRotationAxisAndAngle, float3 positionOnAxis, float3 position) { - float3 pointOnAxis = positionOnAxis + normalizedRotationAxisAndAngle.xyz * dot(normalizedRotationAxisAndAngle.xyz, position - positionOnAxis); - float3 axisU = position - pointOnAxis; - float3 axisV = cross(normalizedRotationAxisAndAngle.xyz, axisU); - float cosAngle, sinAngle; - sincos(normalizedRotationAxisAndAngle.w, sinAngle, cosAngle); - float3 rotation = axisU * cosAngle + axisV * sinAngle; - return pointOnAxis + rotation - position; + float3 pointOnAxis = positionOnAxis + normalizedRotationAxisAndAngle.xyz * dot(normalizedRotationAxisAndAngle.xyz, position - positionOnAxis); + float3 axisU = position - pointOnAxis; + float3 axisV = cross(normalizedRotationAxisAndAngle.xyz, axisU); + float cosAngle, sinAngle; + sincos(normalizedRotationAxisAndAngle.w, sinAngle, cosAngle); + float3 rotation = axisU * cosAngle + axisV * sinAngle; + return pointOnAxis + rotation - position; } #endif diff --git a/Source/Shaders/Matrix.hlsl b/Source/Shaders/Matrix.hlsl index ec611d93c..c1f379e43 100644 --- a/Source/Shaders/Matrix.hlsl +++ b/Source/Shaders/Matrix.hlsl @@ -49,26 +49,26 @@ float3x3 EulerMatrix(float3 angles) { float3 s, c; sincos(angles, s, c); - return float3x3(c.y * c.z + s.x * s.y * s.z, c.z * s.x * s.y - c.y * s.z, c.x * s.y, - c.x * s.z, c.x * c.z, -s.x, - -c.z * s.y + c.y * s.x * s.z, c.y * c.z * s.x + s.y * s.z, c.x * c.y); + return float3x3(c.y * c.z + s.x * s.y * s.z, c.z * s.x * s.y - c.y * s.z, c.x * s.y, c.x * s.z, c.x * c.z, -s.x, -c.z * s.y + c.y * s.x * s.z, c.y * c.z * s.x + s.y * s.z, c.x * c.y); } float4x4 QuaternionToMatrix(float4 q) { - float x2 = q.x + q.x; float y2 = q.y + q.y; float z2 = q.z + q.z; - float xx = q.x * x2; float xy = q.x * y2; float xz = q.x * z2; - float yy = q.y * y2; float yz = q.y * z2; float zz = q.z * z2; - float wx = q.w * x2; float wy = q.w * y2; float wz = q.w * z2; - - float4x4 result = - { - 1.0f - (yy + zz), xy - wz, xz + wy, 0.0f, - xy + wz, 1.0f - (xx + zz), yz - wx, 0.0f, - xz - wy, yz + wx, 1.0f - (xx + yy), 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - return result; + // @formatter:off + float x2 = q.x + q.x; float y2 = q.y + q.y; float z2 = q.z + q.z; + float xx = q.x * x2; float xy = q.x * y2; float xz = q.x * z2; + float yy = q.y * y2; float yz = q.y * z2; float zz = q.z * z2; + float wx = q.w * x2; float wy = q.w * y2; float wz = q.w * z2; + + float4x4 result = + { + 1.0f - (yy + zz), xy - wz, xz + wy, 0.0f, + xy + wz, 1.0f - (xx + zz), yz - wx, 0.0f, + xz - wy, yz + wx, 1.0f - (xx + yy), 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + return result; + // @formatter:on } #endif diff --git a/Source/Shaders/MonteCarlo.hlsl b/Source/Shaders/MonteCarlo.hlsl index b11410957..aefc866cf 100644 --- a/Source/Shaders/MonteCarlo.hlsl +++ b/Source/Shaders/MonteCarlo.hlsl @@ -5,16 +5,16 @@ float3 TangentToWorld(float3 vec, float3 tangentZ) { - float3 upVector = abs(tangentZ.z) < 0.999 ? float3(0,0,1) : float3(1,0,0); - float3 tangentX = normalize(cross(upVector, tangentZ)); - float3 tangentY = cross(tangentZ, tangentX); - return tangentX * vec.x + tangentY * vec.y + tangentZ * vec.z; + float3 upVector = abs(tangentZ.z) < 0.999 ? float3(0, 0, 1) : float3(1, 0, 0); + float3 tangentX = normalize(cross(upVector, tangentZ)); + float3 tangentY = cross(tangentZ, tangentX); + return tangentX * vec.x + tangentY * vec.y + tangentZ * vec.z; } uint ReverseBits32(uint bits) { #if FEATURE_LEVEL >= FEATURE_LEVEL_SM5 - return reversebits(bits); + return reversebits(bits); #else bits = ( bits << 16) | ( bits >> 16); bits = ( (bits & 0x00ff00ff) << 8 ) | ( (bits & 0xff00ff00) >> 8 ); @@ -27,117 +27,117 @@ uint ReverseBits32(uint bits) float2 Hammersley(uint index, uint numSamples, uint2 random) { - float e1 = frac((float)index / numSamples + float(random.x & 0xffff) / (1 << 16)); - float e2 = float(ReverseBits32(index) ^ random.y) * 2.3283064365386963e-10; - return float2(e1, e2); + float e1 = frac((float)index / numSamples + float(random.x & 0xffff) / (1 << 16)); + float e2 = float(ReverseBits32(index) ^ random.y) * 2.3283064365386963e-10; + return float2(e1, e2); } float4 UniformSampleSphere(float2 e) { - float phi = 2 * PI * e.x; - float cosTheta = 1 - 2 * e.y; - float sinTheta = sqrt(1 - cosTheta * cosTheta); + float phi = 2 * PI * e.x; + float cosTheta = 1 - 2 * e.y; + float sinTheta = sqrt(1 - cosTheta * cosTheta); - float3 h; - h.x = sinTheta * cos(phi); - h.y = sinTheta * sin(phi); - h.z = cosTheta; + float3 h; + h.x = sinTheta * cos(phi); + h.y = sinTheta * sin(phi); + h.z = cosTheta; - float pdf = 1.0 / (4 * PI); - return float4(h, pdf); + float pdf = 1.0 / (4 * PI); + return float4(h, pdf); } float4 UniformSampleHemisphere(float2 e) { - float phi = 2 * PI * e.x; - float cosTheta = e.y; - float sinTheta = sqrt(1 - cosTheta * cosTheta); + float phi = 2 * PI * e.x; + float cosTheta = e.y; + float sinTheta = sqrt(1 - cosTheta * cosTheta); - float3 h; - h.x = sinTheta * cos(phi); - h.y = sinTheta * sin(phi); - h.z = cosTheta; + float3 h; + h.x = sinTheta * cos(phi); + h.y = sinTheta * sin(phi); + h.z = cosTheta; - float pdf = 1.0 / (2 * PI); - return float4(h, pdf); + float pdf = 1.0 / (2 * PI); + return float4(h, pdf); } float4 CosineSampleHemisphere(float2 e) { - float phi = 2 * PI * e.x; - float cosTheta = sqrt(e.y); - float sinTheta = sqrt(1 - cosTheta * cosTheta); + float phi = 2 * PI * e.x; + float cosTheta = sqrt(e.y); + float sinTheta = sqrt(1 - cosTheta * cosTheta); - float3 h; - h.x = sinTheta * cos(phi); - h.y = sinTheta * sin(phi); - h.z = cosTheta; + float3 h; + h.x = sinTheta * cos(phi); + h.y = sinTheta * sin(phi); + h.z = cosTheta; - float pdf = cosTheta / PI; - return float4(h, pdf); + float pdf = cosTheta / PI; + return float4(h, pdf); } float4 UniformSampleCone(float2 e, float cosThetaMax) { - float phi = 2 * PI * e.x; - float cosTheta = lerp(cosThetaMax, 1, e.y); - float sinTheta = sqrt(1 - cosTheta * cosTheta); + float phi = 2 * PI * e.x; + float cosTheta = lerp(cosThetaMax, 1, e.y); + float sinTheta = sqrt(1 - cosTheta * cosTheta); - float3 l; - l.x = sinTheta * cos(phi); - l.y = sinTheta * sin(phi); - l.z = cosTheta; + float3 l; + l.x = sinTheta * cos(phi); + l.y = sinTheta * sin(phi); + l.z = cosTheta; - float pdf = 1.0 / (2 * PI * (1 - cosThetaMax)); - return float4(l, pdf); + float pdf = 1.0 / (2 * PI * (1 - cosThetaMax)); + return float4(l, pdf); } float4 ImportanceSampleBlinn(float2 e, float roughness) { - float m = roughness * roughness; - float n = 2 / (m * m) - 2; + float m = roughness * roughness; + float n = 2 / (m * m) - 2; - float phi = 2 * PI * e.x; - float cosTheta = ClampedPow(e.y, 1 / (n + 1)); - float sinTheta = sqrt(1 - cosTheta * cosTheta); + float phi = 2 * PI * e.x; + float cosTheta = ClampedPow(e.y, 1 / (n + 1)); + float sinTheta = sqrt(1 - cosTheta * cosTheta); - float3 h; - h.x = sinTheta * cos(phi); - h.y = sinTheta * sin(phi); - h.z = cosTheta; + float3 h; + h.x = sinTheta * cos(phi); + h.y = sinTheta * sin(phi); + h.z = cosTheta; - float d = (n + 2)/ (2 * PI) * ClampedPow(cosTheta, n); - float pdf = d * cosTheta; - return float4(h, pdf); + float d = (n + 2) / (2 * PI) * ClampedPow(cosTheta, n); + float pdf = d * cosTheta; + return float4(h, pdf); } float4 ImportanceSampleGGX(float2 e, float roughness) { - float m = roughness * roughness; - float m2 = m * m; + float m = roughness * roughness; + float m2 = m * m; - float phi = 2 * PI * e.x; - float cosTheta = sqrt((1 - e.y) / (1 + (m2 - 1) * e.y)); - float sinTheta = sqrt(1 - cosTheta * cosTheta); + float phi = 2 * PI * e.x; + float cosTheta = sqrt((1 - e.y) / (1 + (m2 - 1) * e.y)); + float sinTheta = sqrt(1 - cosTheta * cosTheta); - float3 h; - h.x = sinTheta * cos(phi); - h.y = sinTheta * sin(phi); - h.z = cosTheta; - - float r = (cosTheta * m2 - cosTheta) * cosTheta + 1; - float d = m2 / (PI * r * r); - float pdf = d * cosTheta; - return float4(h, pdf); + float3 h; + h.x = sinTheta * cos(phi); + h.y = sinTheta * sin(phi); + h.z = cosTheta; + + float r = (cosTheta * m2 - cosTheta) * cosTheta + 1; + float d = m2 / (PI * r * r); + float pdf = d * cosTheta; + return float4(h, pdf); } // Multiple importance sampling power heuristic of two functions with a power of two. // [Veach 1997, "Robust Monte Carlo Methods for Light Transport Simulation"] float MISWeight(uint number, float PDF, uint otherNumber, float otherPDF) { - float weight = number * PDF; - float otherWeight = otherNumber * otherPDF; - return weight * weight / (weight * weight + otherWeight * otherWeight); + float weight = number * PDF; + float otherWeight = otherNumber * otherPDF; + return weight * weight / (weight * weight + otherWeight * otherWeight); } #endif diff --git a/Source/Shaders/Quaternion.hlsl b/Source/Shaders/Quaternion.hlsl index 09b5c49b3..1247c48fe 100644 --- a/Source/Shaders/Quaternion.hlsl +++ b/Source/Shaders/Quaternion.hlsl @@ -5,14 +5,14 @@ float4 QuaternionMultiply(float4 q1, float4 q2) { - return float4(q2.xyz * q1.w + q1.xyz * q2.w + cross(q1.xyz, q2.xyz), q1.w * q2.w - dot(q1.xyz, q2.xyz)); + return float4(q2.xyz * q1.w + q1.xyz * q2.w + cross(q1.xyz, q2.xyz), q1.w * q2.w - dot(q1.xyz, q2.xyz)); } float3 QuaternionRotate(float4 q, float3 v) { - float3 b = q.xyz; - float b2 = dot(b, b); - return (v * (q.w * q.w - b2) + b * (dot(v, b) * 2.f) + cross(b, v) * (q.w * 2.f)); + float3 b = q.xyz; + float b2 = dot(b, b); + return (v * (q.w * q.w - b2) + b * (dot(v, b) * 2.f) + cross(b, v) * (q.w * 2.f)); } #endif diff --git a/Source/Shaders/Random.hlsl b/Source/Shaders/Random.hlsl index 33aa3cbb4..0c48b0eaa 100644 --- a/Source/Shaders/Random.hlsl +++ b/Source/Shaders/Random.hlsl @@ -5,8 +5,8 @@ float PseudoRandom(float2 xy) { - float2 p = frac(xy / 128.0f) * 128.0f + float2(-64.340622f, -72.465622f); - return frac(dot(p.xyx * p.xyy, float3(20.390625f, 60.703125f, 2.4281209f))); + float2 p = frac(xy / 128.0f) * 128.0f + float2(-64.340622f, -72.465622f); + return frac(dot(p.xyx * p.xyy, float3(20.390625f, 60.703125f, 2.4281209f))); } // Generic noise (1-component) @@ -23,33 +23,33 @@ float2 RandN2(float2 n) void FindBestAxisVectors(float3 input, out float3 axis1, out float3 axis2) { - const float3 a = abs(input); - if (a.z > a.x && a.z > a.y) - axis1 = float3(1, 0, 0); - else - axis1 = float3(0, 0, 1); - axis1 = normalize(axis1 - input * dot(axis1, input)); - axis2 = cross(axis1, input); + const float3 a = abs(input); + if (a.z > a.x && a.z > a.y) + axis1 = float3(1, 0, 0); + else + axis1 = float3(0, 0, 1); + axis1 = normalize(axis1 - input * dot(axis1, input)); + axis2 = cross(axis1, input); } float PerlinRamp(in float t) { - return t * t * t * (t * (t * 6 - 15) + 10); + return t * t * t * (t * (t * 6 - 15) + 10); } float2 PerlinRamp(in float2 t) { - return t * t * t * (t * (t * 6 - 15) + 10); + return t * t * t * (t * (t * 6 - 15) + 10); } float3 PerlinRamp(in float3 t) { - return t * t * t * (t * (t * 6 - 15) + 10); + return t * t * t * (t * (t * 6 - 15) + 10); } float4 PerlinRamp(in float4 t) { - return t * t * t * (t * (t * 6 - 15) + 10); + return t * t * t * (t * (t * 6 - 15) + 10); } #endif diff --git a/Source/Shaders/ReflectionsCommon.hlsl b/Source/Shaders/ReflectionsCommon.hlsl index cd4695095..f6e4aafa1 100644 --- a/Source/Shaders/ReflectionsCommon.hlsl +++ b/Source/Shaders/ReflectionsCommon.hlsl @@ -7,48 +7,48 @@ float GetSpecularOcclusion(float NoV, float roughnessSq, float ao) { - return saturate(pow(NoV + ao, roughnessSq) - 1 + ao); + return saturate(pow(NoV + ao, roughnessSq) - 1 + ao); } float4 SampleReflectionProbe(float3 viewPos, TextureCube probe, ProbeData data, float3 positionWS, float3 normal, float roughness) { - // Calculate distance from probe to the pixel - float3 captureVector = positionWS - data.ProbePos; - float captureVectorLength = length(captureVector); - - // Check if cannot light pixel - // TODO: maybe remove this check?? - test it out with dozens of probes - BRANCH - if (captureVectorLength >= data.ProbeRadius) - { - // End - return 0; - } + // Calculate distance from probe to the pixel + float3 captureVector = positionWS - data.ProbePos; + float captureVectorLength = length(captureVector); - // Fade out based on distance to capture - float normalizedDistanceToCapture = saturate(captureVectorLength * data.ProbeInvRadius); - float distanceAlpha = 1.0 - smoothstep(0.7, 1, normalizedDistanceToCapture); - float fade = distanceAlpha * data.ProbeBrightness; + // Check if cannot light pixel + // TODO: maybe remove this check?? - test it out with dozens of probes + BRANCH + if (captureVectorLength >= data.ProbeRadius) + { + // End + return 0; + } - // Calculate reflection vector - float3 V = normalize(positionWS - viewPos); - float3 R = reflect(V, normal); - float3 D = data.ProbeInvRadius * captureVector + R; + // Fade out based on distance to capture + float normalizedDistanceToCapture = saturate(captureVectorLength * data.ProbeInvRadius); + float distanceAlpha = 1.0 - smoothstep(0.7, 1, normalizedDistanceToCapture); + float fade = distanceAlpha * data.ProbeBrightness; - // Sample probe at valid mip level based on surface roughness value - half mip = ProbeMipFromRoughness(roughness); - float4 probeSample = probe.SampleLevel(SamplerLinearClamp, D, mip); + // Calculate reflection vector + float3 V = normalize(positionWS - viewPos); + float3 R = reflect(V, normal); + float3 D = data.ProbeInvRadius * captureVector + R; - return probeSample * fade; + // Sample probe at valid mip level based on surface roughness value + half mip = ProbeMipFromRoughness(roughness); + float4 probeSample = probe.SampleLevel(SamplerLinearClamp, D, mip); + + return probeSample * fade; } // Calculates the reflective environment lighting to multiply the raw reflection color for the specular light (eg. from Env Probe or SSR). float3 GetReflectionSpecularLighting(float3 viewPos, GBufferSample gBuffer) { - float3 specularColor = GetSpecularColor(gBuffer); - float3 V = normalize(viewPos - gBuffer.WorldPos); - float NoV = saturate(dot(gBuffer.Normal, V)); - return EnvBRDFApprox(specularColor, gBuffer.Roughness, NoV); + float3 specularColor = GetSpecularColor(gBuffer); + float3 V = normalize(viewPos - gBuffer.WorldPos); + float NoV = saturate(dot(gBuffer.Normal, V)); + return EnvBRDFApprox(specularColor, gBuffer.Roughness, NoV); } #endif diff --git a/Source/Shaders/SH.hlsl b/Source/Shaders/SH.hlsl index d4d38d0fe..93712af03 100644 --- a/Source/Shaders/SH.hlsl +++ b/Source/Shaders/SH.hlsl @@ -6,34 +6,34 @@ // The SH coefficients for the projection of a function that maps directions to scalar values struct ThreeBandSHVector { - half4 V0; - half4 V1; - half V2; + half4 V0; + half4 V1; + half V2; }; ThreeBandSHVector SHBasisFunction3(half3 v) { - ThreeBandSHVector result; + ThreeBandSHVector result; - result.V0.x = 0.282095f; - result.V0.y = -0.488603f * v.y; - result.V0.z = 0.488603f * v.z; - result.V0.w = -0.488603f * v.x; + result.V0.x = 0.282095f; + result.V0.y = -0.488603f * v.y; + result.V0.z = 0.488603f * v.z; + result.V0.w = -0.488603f * v.x; - result.V1.x = 1.092548f * v.x * v.y; - result.V1.y = -1.092548f * v.y * v.z; - result.V1.z = 0.315392f * (3.0f * v.z - 1.0f); - result.V1.w = -1.092548f * v.x * v.z; - result.V2 = 0.546274f * (v.x - v.y); + result.V1.x = 1.092548f * v.x * v.y; + result.V1.y = -1.092548f * v.y * v.z; + result.V1.z = 0.315392f * (3.0f * v.z - 1.0f); + result.V1.w = -1.092548f * v.x * v.z; + result.V2 = 0.546274f * (v.x - v.y); - return result; + return result; } // Projects a direction onto SH and convolves with a cosine kernel to compute irradiance void ProjectOntoSH3(in float3 n, in float3 color, out float3 sh[9]) { - // Cosine kernel - const float A0 = 3.141593f; + // Cosine kernel + const float A0 = 3.141593f; const float A1 = 2.095395f; const float A2 = 0.785398f; @@ -57,27 +57,28 @@ void ProjectOntoSH3(in float3 n, in float3 color, out float3 sh[9]) // [Ralf Habel and Michael Wimmer, "Efficient Irradiance Normal Mapping"] void ConvertSH3ToHBasis(in float3 sh[9], out float3 hBasis[4]) { - const float rt2 = sqrt(2.0f); - const float rt32 = sqrt(3.0f / 2.0f); - const float rt52 = sqrt(5.0f / 2.0f); - const float rt152 = sqrt(15.0f / 2.0f); - const float convMatrix[4*9] = - { - 1.0f / rt2, 0, 0.5f * rt32, 0, 0, 0, 0, 0, 0, - 0, 1.0f / rt2, 0, 0, 0, (3.0f / 8.0f) * rt52, 0, 0, 0, - 0, 0, 1.0f / (2.0f * rt2), 0, 0, 0, 0.25f * rt152, 0, 0, - 0, 0, 0, 1.0f / rt2, 0, 0, 0, (3.0f / 8.0f) * rt52, 0 - }; + const float rt2 = sqrt(2.0f); + const float rt32 = sqrt(3.0f / 2.0f); + const float rt52 = sqrt(5.0f / 2.0f); + const float rt152 = sqrt(15.0f / 2.0f); + const float convMatrix[4 * 9] = + { + // @formatter:off + 1.0f / rt2, 0, 0.5f * rt32, 0, 0, 0, 0, 0, 0, + 0, 1.0f / rt2, 0, 0, 0, (3.0f / 8.0f) * rt52, 0, 0, 0, + 0, 0, 1.0f / (2.0f * rt2), 0, 0, 0, 0.25f * rt152, 0, 0, + 0, 0, 0, 1.0f / rt2, 0, 0, 0, (3.0f / 8.0f) * rt52, 0 + // @formatter:on + }; - UNROLL - for (uint row = 0; row < 4; row++) - { - hBasis[row] = 0.0f; - - UNROLL - for (uint col = 0; col < 9; col++) - hBasis[row] += convMatrix[row * 9 + col] * sh[col]; - } + UNROLL + for (uint row = 0; row < 4; row++) + { + hBasis[row] = 0.0f; + UNROLL + for (uint col = 0; col < 9; col++) + hBasis[row] += convMatrix[row * 9 + col] * sh[col]; + } } #endif diff --git a/Source/Shaders/SSR.hlsl b/Source/Shaders/SSR.hlsl index ccb2b1e89..41c1ccfd5 100644 --- a/Source/Shaders/SSR.hlsl +++ b/Source/Shaders/SSR.hlsl @@ -9,137 +9,134 @@ // 1:-1 to 0:1 float2 ClipToUv(float2 clipPos) { - return clipPos * float2(0.5, -0.5) + float2(0.5, 0.5); + return clipPos * float2(0.5, -0.5) + float2(0.5, 0.5); } // go into clip space (-1:1 from bottom/left to up/right) float3 ProjectWorldToClip(float3 wsPos, float4x4 viewProjectionMatrix) { - float4 clipPos = mul(float4(wsPos, 1), viewProjectionMatrix); - return clipPos.xyz / clipPos.w; + float4 clipPos = mul(float4(wsPos, 1), viewProjectionMatrix); + return clipPos.xyz / clipPos.w; } // go into UV space. (0:1 from top/left to bottom/right) float3 ProjectWorldToUv(float3 wsPos, float4x4 viewProjectionMatrix) { - float3 clipPos = ProjectWorldToClip(wsPos, viewProjectionMatrix); - return float3(ClipToUv(clipPos.xy), clipPos.z); + float3 clipPos = ProjectWorldToClip(wsPos, viewProjectionMatrix); + return float3(ClipToUv(clipPos.xy), clipPos.z); } float3 TangentToWorld(float3 N, float4 H) { - float3 upVector = abs(N.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0); - float3 T = normalize(cross(upVector, N)); - float3 B = cross(N, T); - return float3((T * H.x) + (B * H.y) + (N * H.z)); + float3 upVector = abs(N.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0); + float3 T = normalize(cross(upVector, N)); + float3 B = cross(N, T); + return float3((T * H.x) + (B * H.y) + (N * H.z)); } float RayAttenBorder(float2 pos, float value) { - float borderDist = min(1.0 - max(pos.x, pos.y), min(pos.x, pos.y)); - return saturate(borderDist > value ? 1.0 : borderDist / value); + float borderDist = min(1.0 - max(pos.x, pos.y), min(pos.x, pos.y)); + return saturate(borderDist > value ? 1.0 : borderDist / value); } // Screen Space Reflection ray tracing utility. // Returns: xy: hitUV, z: hitMask, where hitUV is the result UV of hit pixel, hitMask is the normalized sample weight (0 if no hit). float3 TraceSceenSpaceReflection(float2 uv, GBufferSample gBuffer, Texture2D depthBuffer, float3 viewPos, float4x4 viewMatrix, float4x4 viewProjectionMatrix, float stepSize, float maxSamples = 20, bool temporal = false, float temporalTime = 0.0f, float worldAntiSelfOcclusionBias = 0.1f, float brdfBias = 0.82f, float drawDistance = 5000.0f, float roughnessThreshold = 0.4f, float edgeFade = 0.1f) { - // Reject invalid pixels - if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT || gBuffer.Roughness > roughnessThreshold || gBuffer.ViewPos.z > drawDistance) - return 0; + // Reject invalid pixels + if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT || gBuffer.Roughness > roughnessThreshold || gBuffer.ViewPos.z > drawDistance) + return 0; - // Calculate view space normal vector - float3 normalVS = mul(gBuffer.Normal, (float3x3)viewMatrix); + // Calculate view space normal vector + float3 normalVS = mul(gBuffer.Normal, (float3x3)viewMatrix); - // Randomize it a little - float2 jitter = RandN2(uv + temporalTime); - float2 Xi = jitter; - Xi.y = lerp(Xi.y, 0.0, brdfBias); - float3 H = temporal ? TangentToWorld(gBuffer.Normal, ImportanceSampleGGX(Xi, gBuffer.Roughness)) : gBuffer.Normal; + // Randomize it a little + float2 jitter = RandN2(uv + temporalTime); + float2 Xi = jitter; + Xi.y = lerp(Xi.y, 0.0, brdfBias); + float3 H = temporal ? TangentToWorld(gBuffer.Normal, ImportanceSampleGGX(Xi, gBuffer.Roughness)) : gBuffer.Normal; - // Calculate normalized view space reflection vector - float3 reflectVS = normalize(reflect(gBuffer.ViewPos, normalVS)); - if (gBuffer.ViewPos.z < 1.0 && reflectVS.z < 0.4) - return 0; + // Calculate normalized view space reflection vector + float3 reflectVS = normalize(reflect(gBuffer.ViewPos, normalVS)); + if (gBuffer.ViewPos.z < 1.0 && reflectVS.z < 0.4) + return 0; - float3 viewWS = normalize(gBuffer.WorldPos - viewPos); - float3 reflectWS = reflect(viewWS, H.xyz); + float3 viewWS = normalize(gBuffer.WorldPos - viewPos); + float3 reflectWS = reflect(viewWS, H.xyz); - float3 startWS = gBuffer.WorldPos + gBuffer.Normal * worldAntiSelfOcclusionBias; - float3 startUV = ProjectWorldToUv(startWS, viewProjectionMatrix); - float3 endUV = ProjectWorldToUv(startWS + reflectWS, viewProjectionMatrix); + float3 startWS = gBuffer.WorldPos + gBuffer.Normal * worldAntiSelfOcclusionBias; + float3 startUV = ProjectWorldToUv(startWS, viewProjectionMatrix); + float3 endUV = ProjectWorldToUv(startWS + reflectWS, viewProjectionMatrix); - float3 rayUV = endUV - startUV; + float3 rayUV = endUV - startUV; float2 rayUVAbs = abs(rayUV.xy); - rayUV *= stepSize / max(rayUVAbs.x, rayUVAbs.y); - float3 startUv = startUV + rayUV * 2; + rayUV *= stepSize / max(rayUVAbs.x, rayUVAbs.y); + float3 startUv = startUV + rayUV * 2; - float3 currOffset = startUv; - float3 rayStep = rayUV * 2; + float3 currOffset = startUv; + float3 rayStep = rayUV * 2; - // Calculate number of samples - float3 samplesToEdge = ((sign(rayStep.xyz) * 0.5 + 0.5) - currOffset.xyz) / rayStep.xyz; - samplesToEdge.x = min(samplesToEdge.x, min(samplesToEdge.y, samplesToEdge.z)) * 1.05f; - float numSamples = min(maxSamples, samplesToEdge.x); - rayStep *= samplesToEdge.x / numSamples; + // Calculate number of samples + float3 samplesToEdge = ((sign(rayStep.xyz) * 0.5 + 0.5) - currOffset.xyz) / rayStep.xyz; + samplesToEdge.x = min(samplesToEdge.x, min(samplesToEdge.y, samplesToEdge.z)) * 1.05f; + float numSamples = min(maxSamples, samplesToEdge.x); + rayStep *= samplesToEdge.x / numSamples; - // Calculate depth difference error - float depthDiffError = 1.3f * abs(rayStep.z); + // Calculate depth difference error + float depthDiffError = 1.3f * abs(rayStep.z); - // Ray trace - float currSampleIndex = 0; - float currSample, depthDiff; - LOOP - while (currSampleIndex < numSamples) - { - // Sample depth buffer and calculate depth difference - currSample = SAMPLE_RT(depthBuffer, currOffset.xy).r; - depthDiff = currOffset.z - currSample; + // Ray trace + float currSampleIndex = 0; + float currSample, depthDiff; + LOOP + while (currSampleIndex < numSamples) + { + // Sample depth buffer and calculate depth difference + currSample = SAMPLE_RT(depthBuffer, currOffset.xy).r; + depthDiff = currOffset.z - currSample; - // Check intersection - if (depthDiff >= 0) - { - if (depthDiff < depthDiffError) - { - break; - } - else - { - currOffset -= rayStep; - rayStep *= 0.5; - } - } + // Check intersection + if (depthDiff >= 0) + { + if (depthDiff < depthDiffError) + { + break; + } + currOffset -= rayStep; + rayStep *= 0.5; + } - // Move forward - currOffset += rayStep; - currSampleIndex++; - } + // Move forward + currOffset += rayStep; + currSampleIndex++; + } - // Check if has valid result after ray tracing - if (currSampleIndex >= numSamples) - { - // All samples done but no result - return 0; - } + // Check if has valid result after ray tracing + if (currSampleIndex >= numSamples) + { + // All samples done but no result + return 0; + } - float2 hitUV = currOffset.xy; + float2 hitUV = currOffset.xy; - // Fade rays close to screen edge - const float fadeStart = 0.9f; - const float fadeEnd = 1.0f; - const float fadeDiffRcp = 1.0f / (fadeEnd - fadeStart); - float2 boundary = abs(hitUV - float2(0.5f, 0.5f)) * 2.0f; - float fadeOnBorder = 1.0f - saturate((boundary.x - fadeStart) * fadeDiffRcp); - fadeOnBorder *= 1.0f - saturate((boundary.y - fadeStart) * fadeDiffRcp); - fadeOnBorder = smoothstep(0.0f, 1.0f, fadeOnBorder); - fadeOnBorder *= RayAttenBorder(hitUV, edgeFade); + // Fade rays close to screen edge + const float fadeStart = 0.9f; + const float fadeEnd = 1.0f; + const float fadeDiffRcp = 1.0f / (fadeEnd - fadeStart); + float2 boundary = abs(hitUV - float2(0.5f, 0.5f)) * 2.0f; + float fadeOnBorder = 1.0f - saturate((boundary.x - fadeStart) * fadeDiffRcp); + fadeOnBorder *= 1.0f - saturate((boundary.y - fadeStart) * fadeDiffRcp); + fadeOnBorder = smoothstep(0.0f, 1.0f, fadeOnBorder); + fadeOnBorder *= RayAttenBorder(hitUV, edgeFade); - // Fade rays on high roughness - float roughnessFade = saturate((roughnessThreshold - gBuffer.Roughness) * 20); + // Fade rays on high roughness + float roughnessFade = saturate((roughnessThreshold - gBuffer.Roughness) * 20); - // Fade on distance - float distanceFade = saturate((drawDistance - gBuffer.ViewPos.z) / drawDistance); + // Fade on distance + float distanceFade = saturate((drawDistance - gBuffer.ViewPos.z) / drawDistance); - // Output: xy: hitUV, z: hitMask - return float3(hitUV, fadeOnBorder * roughnessFade * distanceFade); + // Output: xy: hitUV, z: hitMask + return float3(hitUV, fadeOnBorder * roughnessFade * distanceFade); } diff --git a/Source/Shaders/ShadowsCommon.hlsl b/Source/Shaders/ShadowsCommon.hlsl index 56764937b..c06b16654 100644 --- a/Source/Shaders/ShadowsCommon.hlsl +++ b/Source/Shaders/ShadowsCommon.hlsl @@ -10,26 +10,26 @@ // Set default macros if not provided #ifndef SHADOWS_QUALITY - #define SHADOWS_QUALITY 0 +#define SHADOWS_QUALITY 0 #endif #ifndef CSM_BLENDING - #define CSM_BLENDING 0 +#define CSM_BLENDING 0 #endif // Structure that contains information about light struct LightShadowData { - float2 ShadowMapSize; - float Sharpness; - float Fade; + float2 ShadowMapSize; + float Sharpness; + float Fade; - float NormalOffsetScale; - float Bias; - float FadeDistance; - uint NumCascades; + float NormalOffsetScale; + float Bias; + float FadeDistance; + uint NumCascades; - float4 CascadeSplits; - float4x4 ShadowVP[6]; + float4 CascadeSplits; + float4x4 ShadowVP[6]; }; #ifdef PLATFORM_ANDROID @@ -41,15 +41,15 @@ struct LightShadowData float3 GetShadowPositionOffset(float offsetScale, float NoL, float3 normal) { - float normalOffsetScale = saturate(1.0f - NoL); - return normal * (offsetScale * normalOffsetScale); + float normalOffsetScale = saturate(1.0f - NoL); + return normal * (offsetScale * normalOffsetScale); } float CalculateSubsurfaceOcclusion(float opacity, float sceneDepth, float shadowMapDepth) { - float thickness = max(sceneDepth - shadowMapDepth, 0); - float occlusion = 1 - thickness * lerp(1.0f, 100.0f, opacity); - return shadowMapDepth > 0.99f ? 1 : occlusion; + float thickness = max(sceneDepth - shadowMapDepth, 0); + float occlusion = 1 - thickness * lerp(1.0f, 100.0f, opacity); + return shadowMapDepth > 0.99f ? 1 : occlusion; } #endif diff --git a/Source/Shaders/ShadowsSampling.hlsl b/Source/Shaders/ShadowsSampling.hlsl index a2088c5f8..bf608a0c5 100644 --- a/Source/Shaders/ShadowsSampling.hlsl +++ b/Source/Shaders/ShadowsSampling.hlsl @@ -14,10 +14,10 @@ // Spot: 2, 5, 12, 29 #if SHADOWS_QUALITY == 0 - #define FilterSizeCSM 2 - #define FilterSizeCube 2 - #define FilterSizeSpot 2 - +#define FilterSizeCSM 2 +#define FilterSizeCube 2 +#define FilterSizeSpot 2 + #elif SHADOWS_QUALITY == 1 #define FilterSizeCSM 3 @@ -46,16 +46,16 @@ // Where: direction = normalize(worldPosition - lightPosition) int GetCubeFaceIndex(float3 direction) { - int cubeFaceIndex; - float3 absDirection = abs(direction); - float maxDirection = max(absDirection.x, max(absDirection.y, absDirection.z)); - if (maxDirection == absDirection.x) - cubeFaceIndex = absDirection.x == direction.x ? 0 : 1; - else if (maxDirection == absDirection.y) - cubeFaceIndex = absDirection.y == direction.y ? 2 : 3; - else - cubeFaceIndex = absDirection.z == direction.z ? 4 : 5; - return cubeFaceIndex; + int cubeFaceIndex; + float3 absDirection = abs(direction); + float maxDirection = max(absDirection.x, max(absDirection.y, absDirection.z)); + if (maxDirection == absDirection.x) + cubeFaceIndex = absDirection.x == direction.x ? 0 : 1; + else if (maxDirection == absDirection.y) + cubeFaceIndex = absDirection.y == direction.y ? 2 : 3; + else + cubeFaceIndex = absDirection.z == direction.z ? 4 : 5; + return cubeFaceIndex; } // Samples the shadow map with a fixed-size PCF kernel optimized with GatherCmpRed. @@ -64,15 +64,15 @@ float SampleShadowMapFixedSizePCF(Texture2DArray shadowMap, float2 shadowMapSize { #if FilterSizeCSM == 2 - #if FEATURE_LEVEL >= FEATURE_LEVEL_SM5 +#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5 - return shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, float3(shadowPos.xy, cascadeIndex), sceneDepth); + return shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, float3(shadowPos.xy, cascadeIndex), sceneDepth); - #else +#else return sceneDepth < shadowMap.SampleLevel(SamplerLinearClamp, float3(shadowPos.xy, cascadeIndex), 0).r; - #endif +#endif #else @@ -120,11 +120,11 @@ float SampleShadowMapFixedSizePCF(Texture2DArray shadowMap, float2 shadowMapSize if (value != 0.0f) { // Gather returns xyzw which is counter clockwise order starting with the sample to the lower left of the queried location - #if CAN_USE_GATHER +#if CAN_USE_GATHER v1[(col + FS_2) / 2] = shadowMap.GatherCmp(ShadowSampler, baseUV, sceneDepth, int2(col, row)); - #else +#else float4 gather; @@ -135,7 +135,7 @@ float SampleShadowMapFixedSizePCF(Texture2DArray shadowMap, float2 shadowMapSize v1[(col + FS_2) / 2] = gather; - #endif +#endif } else v1[(col + FS_2) / 2] = 0.0f; @@ -242,7 +242,7 @@ float SampleShadowMapOptimizedPCF(Texture2DArray shadowMap, float2 shadowMapSize #if FilterSizeCSM == 2 - return shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, float3(shadowPos.xy, cascadeIndex), sceneDepth); + return shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, float3(shadowPos.xy, cascadeIndex), sceneDepth); #elif FilterSizeCSM == 3 @@ -265,7 +265,7 @@ float SampleShadowMapOptimizedPCF(Texture2DArray shadowMap, float2 shadowMapSize return sum * 1.0f / 16; - #elif FilterSizeCSM == 5 +#elif FilterSizeCSM == 5 float uw0 = (4 - 3 * s); float uw1 = 7; @@ -347,41 +347,41 @@ float SampleShadowMapOptimizedPCF(Texture2DArray shadowMap, float2 shadowMapSize // Samples the shadow from the shadow map cascade float SampleShadowCascade(Texture2DArray shadowMap, float2 shadowMapSize, float sceneDepth, float2 shadowPosition, uint cascadeIndex) { - float shadow = SampleShadowMapFixedSizePCF(shadowMap, shadowMapSize, sceneDepth, shadowPosition, cascadeIndex); - //float shadow = SampleShadowMapOptimizedPCF(shadowMap, shadowMapSize, sceneDepth, shadowPosition, cascadeIndex); - return shadow; + float shadow = SampleShadowMapFixedSizePCF(shadowMap, shadowMapSize, sceneDepth, shadowPosition, cascadeIndex); + //float shadow = SampleShadowMapOptimizedPCF(shadowMap, shadowMapSize, sceneDepth, shadowPosition, cascadeIndex); + return shadow; } // Samples the shadow for the given directional light (cascaded shadow map sampling) float SampleShadow(LightData light, LightShadowData shadow, Texture2DArray shadowMap, float3 worldPosition, float viewDepth) { - // Create a blend factor which is one before and at the fade plane - float fade = saturate((viewDepth - shadow.CascadeSplits[shadow.NumCascades - 1] + shadow.FadeDistance) / shadow.FadeDistance); - BRANCH - if (fade >= 1.0) - { - return 1; - } + // Create a blend factor which is one before and at the fade plane + float fade = saturate((viewDepth - shadow.CascadeSplits[shadow.NumCascades - 1] + shadow.FadeDistance) / shadow.FadeDistance); + BRANCH + if (fade >= 1.0) + { + return 1; + } - // Figure out which cascade to sample from - uint cascadeIndex = 0; - for (uint i = 0; i < shadow.NumCascades - 1; i++) - { - if (viewDepth > shadow.CascadeSplits[i]) - cascadeIndex = i + 1; - } + // Figure out which cascade to sample from + uint cascadeIndex = 0; + for (uint i = 0; i < shadow.NumCascades - 1; i++) + { + if (viewDepth > shadow.CascadeSplits[i]) + cascadeIndex = i + 1; + } // Project into shadow space - float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[cascadeIndex]); - shadowPosition.xy /= shadowPosition.w; - shadowPosition.z -= shadow.Bias; + float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[cascadeIndex]); + shadowPosition.xy /= shadowPosition.w; + shadowPosition.z -= shadow.Bias; - // Sample shadow - float result = SampleShadowCascade(shadowMap, shadow.ShadowMapSize, shadowPosition.z, shadowPosition.xy, cascadeIndex); + // Sample shadow + float result = SampleShadowCascade(shadowMap, shadow.ShadowMapSize, shadowPosition.z, shadowPosition.xy, cascadeIndex); - // Increase the sharpness for higher cascades to match the filter radius - const float SharpnessScale[MaxNumCascades] = { 1.0f, 1.5f, 3.0f, 3.5f }; - float sharpness = shadow.Sharpness * SharpnessScale[cascadeIndex]; + // Increase the sharpness for higher cascades to match the filter radius + const float SharpnessScale[MaxNumCascades] = { 1.0f, 1.5f, 3.0f, 3.5f }; + float sharpness = shadow.Sharpness * SharpnessScale[cascadeIndex]; #if CSM_BLENDING // Sample the next cascade, and blend between the two results to smooth the transition @@ -408,33 +408,33 @@ float SampleShadow(LightData light, LightShadowData shadow, Texture2DArray shado } #endif - // Apply shadow fade and sharpness - result = saturate((result - 0.5) * sharpness + 0.5); - result = lerp(1.0f, result, (1 - fade) * shadow.Fade); - return result; + // Apply shadow fade and sharpness + result = saturate((result - 0.5) * sharpness + 0.5); + result = lerp(1.0f, result, (1 - fade) * shadow.Fade); + return result; } // Samples the shadow for the given directional light (cascaded shadow map sampling) for the material surface (supports subsurface shadowing) float SampleShadow(LightData light, LightShadowData shadow, Texture2DArray shadowMap, GBufferSample gBuffer, out float subsurfaceShadow) { - subsurfaceShadow = 1; + subsurfaceShadow = 1; - // Create a blend factor which is one before and at the fade plane - float viewDepth = gBuffer.ViewPos.z; - float fade = saturate((viewDepth - shadow.CascadeSplits[shadow.NumCascades - 1] + shadow.FadeDistance) / shadow.FadeDistance); - BRANCH - if (fade >= 1.0) - { - return 1; - } + // Create a blend factor which is one before and at the fade plane + float viewDepth = gBuffer.ViewPos.z; + float fade = saturate((viewDepth - shadow.CascadeSplits[shadow.NumCascades - 1] + shadow.FadeDistance) / shadow.FadeDistance); + BRANCH + if (fade >= 1.0) + { + return 1; + } - // Figure out which cascade to sample from - uint cascadeIndex = 0; - for (uint i = 0; i < shadow.NumCascades - 1; i++) - { - if (viewDepth > shadow.CascadeSplits[i]) - cascadeIndex = i + 1; - } + // Figure out which cascade to sample from + uint cascadeIndex = 0; + for (uint i = 0; i < shadow.NumCascades - 1; i++) + { + if (viewDepth > shadow.CascadeSplits[i]) + cascadeIndex = i + 1; + } #if defined(USE_GBUFFER_CUSTOM_DATA) // Subsurface shadowing @@ -458,51 +458,51 @@ float SampleShadow(LightData light, LightShadowData shadow, Texture2DArray shado } #endif - // Skip if surface is in a full shadow - float NoL = dot(gBuffer.Normal, light.Direction); - BRANCH - if (NoL <= 0) - { - return 0; - } + // Skip if surface is in a full shadow + float NoL = dot(gBuffer.Normal, light.Direction); + BRANCH + if (NoL <= 0) + { + return 0; + } - // Apply normal offset bias - float3 samplePosWS = gBuffer.WorldPos; - samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal); + // Apply normal offset bias + float3 samplePosWS = gBuffer.WorldPos; + samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal); - // Sample shadow - return SampleShadow(light, shadow, shadowMap, samplePosWS, viewDepth); + // Sample shadow + return SampleShadow(light, shadow, shadowMap, samplePosWS, viewDepth); } // Samples the shadow for the given spot light (PCF shadow map sampling) float SampleShadow(LightData light, LightShadowData shadow, Texture2D shadowMap, float3 worldPosition) { - float3 toLight = light.Position - worldPosition; - float toLightLength = length(toLight); - float3 L = toLight / toLightLength; - float dirCheck = dot(-light.Direction, L); + float3 toLight = light.Position - worldPosition; + float toLightLength = length(toLight); + float3 L = toLight / toLightLength; + float dirCheck = dot(-light.Direction, L); - // Skip pixels outside of the light influence - BRANCH - if (toLightLength > light.Radius || dirCheck < 0) - { - return 1; - } + // Skip pixels outside of the light influence + BRANCH + if (toLightLength > light.Radius || dirCheck < 0) + { + return 1; + } - // Negate direction and use normalized value - toLight = -L; + // Negate direction and use normalized value + toLight = -L; - // Project into shadow space - float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[0]); - shadowPosition.z -= shadow.Bias; - shadowPosition.xyz /= shadowPosition.w; + // Project into shadow space + float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[0]); + shadowPosition.z -= shadow.Bias; + shadowPosition.xyz /= shadowPosition.w; - float2 shadowMapUVs = shadowPosition.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f); + float2 shadowMapUVs = shadowPosition.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f); -#if FilterSizeSpot == 2 +#if FilterSizeSpot == 2 - // Use single hardware sample with filtering - float result = shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, shadowMapUVs, shadowPosition.z); + // Use single hardware sample with filtering + float result = shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, shadowMapUVs, shadowPosition.z); #else @@ -525,28 +525,28 @@ float SampleShadow(LightData light, LightShadowData shadow, Texture2D shadowMap, #endif - // Apply shadow fade and sharpness - result = saturate((result - 0.5) * shadow.Sharpness + 0.5); - result = lerp(1.0f, result, shadow.Fade); + // Apply shadow fade and sharpness + result = saturate((result - 0.5) * shadow.Sharpness + 0.5); + result = lerp(1.0f, result, shadow.Fade); - return result; + return result; } // Samples the shadow for the given spot light (PCF shadow map sampling) for the material surface (supports subsurface shadowing) float SampleShadow(LightData light, LightShadowData shadow, Texture2D shadowMap, GBufferSample gBuffer, out float subsurfaceShadow) { - subsurfaceShadow = 1; - float3 toLight = light.Position - gBuffer.WorldPos; - float toLightLength = length(toLight); - float3 L = toLight / toLightLength; - float dirCheck = dot(-light.Direction, L); + subsurfaceShadow = 1; + float3 toLight = light.Position - gBuffer.WorldPos; + float toLightLength = length(toLight); + float3 L = toLight / toLightLength; + float dirCheck = dot(-light.Direction, L); - // Skip pixels outside of the light influence - BRANCH - if (toLightLength > light.Radius || dirCheck < 0) - { - return 1; - } + // Skip pixels outside of the light influence + BRANCH + if (toLightLength > light.Radius || dirCheck < 0) + { + return 1; + } #if defined(USE_GBUFFER_CUSTOM_DATA) // Subsurface shadowing @@ -570,51 +570,51 @@ float SampleShadow(LightData light, LightShadowData shadow, Texture2D shadowMap, } #endif - // Skip if surface is in a full shadow - float NoL = dot(gBuffer.Normal, L); - BRANCH - if (NoL <= 0) - { - return 0; - } + // Skip if surface is in a full shadow + float NoL = dot(gBuffer.Normal, L); + BRANCH + if (NoL <= 0) + { + return 0; + } - // Apply normal offset bias - float3 samplePosWS = gBuffer.WorldPos; - samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal); + // Apply normal offset bias + float3 samplePosWS = gBuffer.WorldPos; + samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal); - // Sample shadow - return SampleShadow(light, shadow, shadowMap, samplePosWS); + // Sample shadow + return SampleShadow(light, shadow, shadowMap, samplePosWS); } // Samples the shadow for the given point light (PCF shadow map sampling) float SampleShadow(LightData light, LightShadowData shadow, TextureCube shadowMap, float3 worldPosition) { - float3 toLight = light.Position - worldPosition; - float toLightLength = length(toLight); - float3 L = toLight / toLightLength; + float3 toLight = light.Position - worldPosition; + float toLightLength = length(toLight); + float3 L = toLight / toLightLength; - // Skip pixels outside of the light influence - BRANCH - if (toLightLength > light.Radius) - { - return 1; - } + // Skip pixels outside of the light influence + BRANCH + if (toLightLength > light.Radius) + { + return 1; + } - // Negate direction and use normalized value - toLight = -L; + // Negate direction and use normalized value + toLight = -L; - // Figure out which cube face we're sampling from - int cubeFaceIndex = GetCubeFaceIndex(toLight); + // Figure out which cube face we're sampling from + int cubeFaceIndex = GetCubeFaceIndex(toLight); - // Project into shadow space - float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[cubeFaceIndex]); - shadowPosition.z -= shadow.Bias; - shadowPosition.xyz /= shadowPosition.w; + // Project into shadow space + float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[cubeFaceIndex]); + shadowPosition.z -= shadow.Bias; + shadowPosition.xyz /= shadowPosition.w; -#if FilterSizeCube == 2 +#if FilterSizeCube == 2 - // Use single hardware sample with filtering - float result = shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, toLight, shadowPosition.z); + // Use single hardware sample with filtering + float result = shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, toLight, shadowPosition.z); #else @@ -637,33 +637,33 @@ float SampleShadow(LightData light, LightShadowData shadow, TextureCube s #endif - // Apply shadow fade and sharpness - result = saturate((result - 0.5) * shadow.Sharpness + 0.5); - result = lerp(1.0f, result, shadow.Fade); + // Apply shadow fade and sharpness + result = saturate((result - 0.5) * shadow.Sharpness + 0.5); + result = lerp(1.0f, result, shadow.Fade); - return result; + return result; } // Samples the shadow for the given point light (PCF shadow map sampling) for the material surface (supports subsurface shadowing) float SampleShadow(LightData light, LightShadowData shadow, TextureCube shadowMap, GBufferSample gBuffer, out float subsurfaceShadow) { - subsurfaceShadow = 1; - float3 toLight = light.Position - gBuffer.WorldPos; - float toLightLength = length(toLight); - float3 L = toLight / toLightLength; + subsurfaceShadow = 1; + float3 toLight = light.Position - gBuffer.WorldPos; + float toLightLength = length(toLight); + float3 L = toLight / toLightLength; - // Skip pixels outside of the light influence - BRANCH - if (toLightLength > light.Radius) - { - return 1; - } + // Skip pixels outside of the light influence + BRANCH + if (toLightLength > light.Radius) + { + return 1; + } - // Negate direction and use normalized value - toLight = -L; + // Negate direction and use normalized value + toLight = -L; - // Figure out which cube face we're sampling from - int cubeFaceIndex = GetCubeFaceIndex(toLight); + // Figure out which cube face we're sampling from + int cubeFaceIndex = GetCubeFaceIndex(toLight); #if defined(USE_GBUFFER_CUSTOM_DATA) // Subsurface shadowing @@ -688,19 +688,19 @@ float SampleShadow(LightData light, LightShadowData shadow, TextureCube s #endif // Skip if surface is in a full shadow - float NoL = dot(gBuffer.Normal, L); - BRANCH - if (NoL <= 0) - { - return 0; - } + float NoL = dot(gBuffer.Normal, L); + BRANCH + if (NoL <= 0) + { + return 0; + } - // Apply normal offset bias - float3 samplePosWS = gBuffer.WorldPos; - samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal); + // Apply normal offset bias + float3 samplePosWS = gBuffer.WorldPos; + samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal); - // Sample shadow - return SampleShadow(light, shadow, shadowMap, samplePosWS); + // Sample shadow + return SampleShadow(light, shadow, shadowMap, samplePosWS); } #endif diff --git a/Source/Shaders/VolumetricFog.shader b/Source/Shaders/VolumetricFog.shader index 90f280838..01756fe56 100644 --- a/Source/Shaders/VolumetricFog.shader +++ b/Source/Shaders/VolumetricFog.shader @@ -286,7 +286,7 @@ Texture3D LightScatteringHistory : register(t2); Texture3D LocalShadowedLightScattering : register(t3); Texture2DArray ShadowMapCSM : register(t4); #if USE_DDGI -Texture2D ProbesState : register(t5); +Texture2D ProbesState : register(t5); Texture2D ProbesDistance : register(t6); Texture2D ProbesIrradiance : register(t7); #else @@ -337,7 +337,7 @@ void CS_LightScattering(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_ #if USE_DDGI // Dynamic Diffuse Global Illumination - float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesState, ProbesDistance, ProbesIrradiance, positionWS, cameraVectorNormalized, 1.0f); + float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesState, ProbesDistance, ProbesIrradiance, positionWS, cameraVectorNormalized, 0.0f, cellOffset.x); lightScattering += float4(irradiance, 1); #else // Sky light